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
}
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 , " \n " , to ) } else if traceOptions . Timeout > 0 { timeout = time . After ( traceOptions . Timeout ) t . Logf ( LogDebug , " \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 , " \n " , tid ) if traceOptions . CallerOnly { options = t . AddThreadFilter ( options , tid ) } if traceOptions . Faulted { options = t . AddFaultedThread ( options , tid ) } } else { t . Logf ( LogWarning , " \n " , err ) } } // Report caller's goid var buf [ 64 ] byte n := runtime . Stack ( buf [ : ] , false ) idField := strings . Fields ( strings . TrimPrefix ( string ( buf [ : n ] ) , " " ) ) [ 0 ] if goid , err := strconv . Atoi ( idField ) ; err == nil { t . Logf ( LogDebug , " \n " , goid ) options = t . AddCallerGo ( options , goid ) } else { t . Logf ( LogWarning , " \n " , err ) } if e != nil { options = t . AddKV ( options , " " , 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 ( " " ) t . Logf ( LogError , " \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 , " \n " , tracer ) var res tracerResult res . stdOut , res . err = tracer . Output ( ) done <- res t . Logf ( LogDebug , " \n " ) } ( ) t . Logf ( LogDebug , " \n " ) var res tracerResult select { case <- timeout : if err = tracer . Process . Kill ( ) ; err != nil { t . Logf ( LogError , " \n " , err ) if kfPanic { t . Logf ( LogWarning , " " + " \n " ) panic ( err ) } } err = errors . New ( " " ) t . Logf ( LogError , " \n " , err ) return case res = <- done : break } // Tracer execution has completed by this point. if res . err != nil { t . Logf ( LogError , " \n " , res . err ) err = res . err return } if t . PutOnTrace ( ) == false { t . Logf ( LogDebug , " \n " ) return } putFn := func ( ) error { t . Logf ( LogDebug , " " ) if err := t . Put ( res . stdOut ) ; err != nil { t . Logf ( LogError , " " , err ) return err } t . Logf ( LogDebug , " \n " ) return nil } if synchronousPut { err = putFn ( ) } else { t . Logf ( LogDebug , " \n " ) if traceOptions . SpawnedGs != nil { traceOptions . SpawnedGs . Add ( 1 ) } go func ( ) { if traceOptions . SpawnedGs != nil { defer traceOptions . SpawnedGs . Done ( ) } putFn ( ) } ( ) } t . Logf ( LogDebug , " \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.
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 .