bcd.go
func Trace(t Tracer, e error, traceOptions *TraceOptions) (err error) {
	if traceOptions == nil {
		traceOptions = t.DefaultTraceOptions()
	}

	// If no timeouts are specified, the timeout channel will block
	// forever (i.e. it will return only after the tracer exits).
	// We create the timer first to account for the work below, but
	// we won't wrap setup in a timeout as it's unlikely to be
	// a bottleneck.
	var timeout <-chan time.Time

	if traceOptions.Timeout == 0 {
		to := t.DefaultTraceOptions().Timeout
		timeout = time.After(to)
		t.Logf(LogDebug, "Tracer timeout: %v\n", to)
	} else if traceOptions.Timeout > 0 {
		timeout = time.After(traceOptions.Timeout)
		t.Logf(LogDebug, "Tracer timeout: %v\n", traceOptions.Timeout)
	}

	// We create a new options slice to avoid modifying the base
	// set of tracer options just for this particular trace
	// invocation.
	options := t.Options()

	// If the caller has requested a trace with thread-specific options,
	// then add the relevant thread specifications to the options list.
	if traceOptions.CallerOnly || traceOptions.Faulted {
		runtime.LockOSThread()
		defer runtime.UnlockOSThread()

		if tid, err := gettid(); err == nil {
			t.Logf(LogDebug, "Retrieved tid: %v\n", tid)

			if traceOptions.CallerOnly {
				options = t.AddThreadFilter(options, tid)
			}

			if traceOptions.Faulted {
				options = t.AddFaultedThread(options, tid)
			}
		} else {
			t.Logf(LogWarning, "Failed to retrieve tid: %v\n", err)
		}
	}

	// Report caller's goid
	var buf [64]byte
	n := runtime.Stack(buf[:], false)
	idField := strings.Fields(strings.TrimPrefix(string(buf[:n]), "goroutine "))[0]
	if goid, err := strconv.Atoi(idField); err == nil {
		t.Logf(LogDebug, "Retrieved goid: %v\n", goid)
		options = t.AddCallerGo(options, goid)
	} else {
		t.Logf(LogWarning, "Failed to retrieve goid: %v\n", err)
	}

	if e != nil {
		options = t.AddKV(options, "error", e.Error())
		if traceOptions.ErrClassification {
			options = t.AddClassifier(options,
				reflect.TypeOf(e).String())
		}
	}

	for _, c := range traceOptions.Classifications {
		options = t.AddClassifier(options, c)
	}

	state.m.RLock()
	kfPanic := state.c.PanicOnKillFailure
	rl := state.c.RateLimit
	synchronousPut := state.c.SynchronousPut
	state.m.RUnlock()

	select {
	case <-timeout:
		err = errors.New("Tracer lock acquisition timed out")
		t.Logf(LogError, "%v\n", err)

		return
	case <-traceLock:
		break
	}

	// We now hold the trace lock.
	// Allow another tracer to execute (i.e. by re-populating the
	// traceLock channel) as long as the current tracer has
	// exited.
	defer func() {
		go traceUnlockRL(t, rl)
	}()

	done := make(chan tracerResult, 1)
	tracer := t.Finalize(options)

	if traceOptions.SpawnedGs != nil {
		traceOptions.SpawnedGs.Add(1)
	}

	go func() {
		if traceOptions.SpawnedGs != nil {
			defer traceOptions.SpawnedGs.Done()
		}

		t.Logf(LogDebug, "Starting tracer %v\n", tracer)

		var res tracerResult

		res.stdOut, res.err = tracer.Output()
		done <- res

		t.Logf(LogDebug, "Tracer finished execution\n")
	}()

	t.Logf(LogDebug, "Waiting for tracer completion...\n")

	var res tracerResult

	select {
	case <-timeout:
		if err = tracer.Process.Kill(); err != nil {
			t.Logf(LogError,
				"Failed to kill tracer upon timeout: %v\n",
				err)

			if kfPanic {
				t.Logf(LogWarning,
					"PanicOnKillFailure set; "+
						"panicking\n")
				panic(err)
			}
		}

		err = errors.New("Tracer execution timed out")
		t.Logf(LogError, "%v; process killed\n", err)

		return
	case res = <-done:
		break
	}

	// Tracer execution has completed by this point.
	if res.err != nil {
		t.Logf(LogError, "Tracer failed to run: %v\n",
			res.err)
		err = res.err

		return
	}

	if t.PutOnTrace() == false {
		t.Logf(LogDebug, "Trace request complete\n")

		return
	}

	putFn := func() error {
		t.Logf(LogDebug, "Uploading snapshot...")

		if err := t.Put(res.stdOut); err != nil {
			t.Logf(LogError, "Failed to upload snapshot: %s",
				err)

			return err
		}

		t.Logf(LogDebug, "Successfully uploaded snapshot\n")

		return nil
	}

	if synchronousPut {
		err = putFn()
	} else {
		t.Logf(LogDebug, "Starting asynchronous put...\n")

		if traceOptions.SpawnedGs != nil {
			traceOptions.SpawnedGs.Add(1)
		}

		go func() {
			if traceOptions.SpawnedGs != nil {
				defer traceOptions.SpawnedGs.Done()
			}

			putFn()
		}()
	}

	t.Logf(LogDebug, "Trace request complete\n")

	return
}
func Trace(t Tracer, e error, traceOptions *TraceOptions) (err error) {
	if traceOptions == nil {
		traceOptions = t.DefaultTraceOptions()
	}

	// If no timeouts are specified, the timeout channel will block
	// forever (i.e. it will return only after the tracer exits).
	// We create the timer first to account for the work below, but
	// we won't wrap setup in a timeout as it's unlikely to be
	// a bottleneck.
	var timeout <-chan time.Time

	if traceOptions.Timeout == 0 {
		to := t.DefaultTraceOptions().Timeout
		timeout = time.After(to)
		t.Logf(LogDebug, "Tracer timeout: %v\n", to)
	} else if traceOptions.Timeout > 0 {
		timeout = time.After(traceOptions.Timeout)
		t.Logf(LogDebug, "Tracer timeout: %v\n", traceOptions.Timeout)
	}

	// We create a new options slice to avoid modifying the base
	// set of tracer options just for this particular trace
	// invocation.
	options := t.Options()

	// If the caller has requested a trace with thread-specific options,
	// then add the relevant thread specifications to the options list.
	if traceOptions.CallerOnly || traceOptions.Faulted {
		runtime.LockOSThread()
		defer runtime.UnlockOSThread()

		if tid, err := gettid(); err == nil {
			t.Logf(LogDebug, "Retrieved tid: %v\n", tid)

			if traceOptions.CallerOnly {
				options = t.AddThreadFilter(options, tid)
			}

			if traceOptions.Faulted {
				options = t.AddFaultedThread(options, tid)
			}
		} else {
			t.Logf(LogWarning, "Failed to retrieve tid: %v\n", err)
		}
	}

	// Report caller's goid
	var buf [64]byte
	n := runtime.Stack(buf[:], false)
	idField := strings.Fields(strings.TrimPrefix(string(buf[:n]), "goroutine "))[0]
	if goid, err := strconv.Atoi(idField); err == nil {
		t.Logf(LogDebug, "Retrieved goid: %v\n", goid)
		options = t.AddCallerGo(options, goid)
	} else {
		t.Logf(LogWarning, "Failed to retrieve goid: %v\n", err)
	}

	if e != nil {
		options = t.AddKV(options, "error", e.Error())
		if traceOptions.ErrClassification {
			options = t.AddClassifier(options,
				reflect.TypeOf(e).String())
		}
	}

	for _, c := range traceOptions.Classifications {
		options = t.AddClassifier(options, c)
	}

	state.m.RLock()
	kfPanic := state.c.PanicOnKillFailure
	rl := state.c.RateLimit
	synchronousPut := state.c.SynchronousPut
	state.m.RUnlock()

	select {
	case <-timeout:
		err = errors.New("Tracer lock acquisition timed out")
		t.Logf(LogError, "%v\n", err)

		return
	case <-traceLock:
		break
	}

	// We now hold the trace lock.
	// Allow another tracer to execute (i.e. by re-populating the
	// traceLock channel) as long as the current tracer has
	// exited.
	defer func() {
		go traceUnlockRL(t, rl)
	}()

	done := make(chan tracerResult, 1)
	tracer := t.Finalize(options)

	if traceOptions.SpawnedGs != nil {
		traceOptions.SpawnedGs.Add(1)
	}

	go func() {
		if traceOptions.SpawnedGs != nil {
			defer traceOptions.SpawnedGs.Done()
		}

		t.Logf(LogDebug, "Starting tracer %v\n", tracer)

		var res tracerResult

		res.stdOut, res.err = tracer.Output()
		done <- res

		t.Logf(LogDebug, "Tracer finished execution\n")
	}()

	t.Logf(LogDebug, "Waiting for tracer completion...\n")

	var res tracerResult

	select {
	case <-timeout:
		if err = tracer.Process.Kill(); err != nil {
			t.Logf(LogError,
				"Failed to kill tracer upon timeout: %v\n",
				err)

			if kfPanic {
				t.Logf(LogWarning,
					"PanicOnKillFailure set; "+
						"panicking\n")
				panic(err)
			}
		}

		err = errors.New("Tracer execution timed out")
		t.Logf(LogError, "%v; process killed\n", err)

		return
	case res = <-done:
		break
	}

	// Tracer execution has completed by this point.
	if res.err != nil {
		t.Logf(LogError, "Tracer failed to run: %v\n",
			res.err)
		err = res.err

		return
	}

	if t.PutOnTrace() == false {
		t.Logf(LogDebug, "Trace request complete\n")

		return
	}

	putFn := func() error {
		t.Logf(LogDebug, "Uploading snapshot...")

		if err := t.Put(res.stdOut); err != nil {
			t.Logf(LogError, "Failed to upload snapshot: %s",
				err)

			return err
		}

		t.Logf(LogDebug, "Successfully uploaded snapshot\n")

		return nil
	}

	if synchronousPut {
		err = putFn()
	} else {
		t.Logf(LogDebug, "Starting asynchronous put...\n")

		if traceOptions.SpawnedGs != nil {
			traceOptions.SpawnedGs.Add(1)
		}

		go func() {
			if traceOptions.SpawnedGs != nil {
				defer traceOptions.SpawnedGs.Done()
			}

			putFn()
		}()
	}

	t.Logf(LogDebug, "Trace request complete\n")

	return
}
// Executes the specified Tracer on the current process.
//
// If e is non-nil, it will be used to augment the trace according to the
// TraceOptions.
// If traceOptions is non-nil, it will be used instead of the Tracer's
// DefaultTraceOptions(). See TraceOptions for details on the various options.
//
// This is goroutine-safe; multiple goroutines may share the same Tracer and
// execute Trace() concurrently. Only one tracer will be allowed to run at
// any point; others will wait to acquire resources (locks) or timeout (if
// timeouts are not disabled). Trace execution will be rate-limited according
// to the GlobalConfig settings.
//
// This may also be called in a new goroutine via go Trace(...). In that case,
// ensure TraceOptions.CallerOnly is false (you will likely also want to set
// TraceOptions.Faulted to false); otherwise, only the newly spawned goroutine
// will be traced.
//
// Output of specific Tracer execution depends on the implementation; most
// Tracers will have options for specifying output paths.