protected override string CreateBody(ThreadSample threadSample) { // The stack follows the experimental GDI conventions described at // https://github.com/signalfx/gdi-specification/blob/29cbcbc969531d50ccfd0b6a4198bb8a89cedebb/specification/semantic_conventions.md#logrecord-message-fields var stackTraceBuilder = new StringBuilder(); stackTraceBuilder.AppendFormat( CultureInfo.InvariantCulture, "\"{0}\" #{1} prio=0 os_prio=0 cpu=0 elapsed=0 tid=0x{2:x} nid=0x{3:x}\n", threadSample.Timestamp, threadSample.ThreadIndex, threadSample.ManagedId, threadSample.NativeId); // TODO Splunk: APMI-2565 here should go Thread state, equivalent of" java.lang.Thread.State: TIMED_WAITING (sleeping)" stackTraceBuilder.Append("\n"); foreach (var frame in threadSample.Frames) { stackTraceBuilder.Append("\tat "); stackTraceBuilder.Append(frame); stackTraceBuilder.Append('\n'); } return(stackTraceBuilder.ToString()); }
protected override string CreateBody(ThreadSample threadSample) { var pprof = new Pprof(); var sampleBuilder = new SampleBuilder(); pprof.AddLabel(sampleBuilder, "source.event.time", threadSample.Timestamp.Milliseconds); if (threadSample.SpanId != 0 || threadSample.TraceIdHigh != 0 || threadSample.TraceIdLow != 0) { pprof.AddLabel(sampleBuilder, "span_id", threadSample.SpanId); pprof.AddLabel(sampleBuilder, "trace_id", TraceIdHelper.ToString(threadSample.TraceIdHigh, threadSample.TraceIdLow)); } foreach (var methodName in threadSample.Frames) { sampleBuilder.AddLocationId(pprof.GetLocationId(methodName)); } pprof.AddLabel(sampleBuilder, "thread.id", threadSample.ManagedId); pprof.AddLabel(sampleBuilder, "thread.name", threadSample.ThreadName); pprof.AddLabel(sampleBuilder, "thread.os.id", threadSample.NativeId); pprof.Profile.Samples.Add(sampleBuilder.Build()); return(Serialize(pprof.Profile)); }
protected abstract string CreateBody(ThreadSample threadSample);
/// <summary> /// Parses the thread sample batch. /// </summary> internal List <ThreadSample> Parse() { uint batchThreadIndex = 0; var samples = new List <ThreadSample>(); long sampleStartMillis = 0; while (_position < _length) { var operationCode = _buffer[_position]; _position++; if (operationCode == OpCodes.StartBatch) { var version = ReadInt(); if (version != 1) { return(null); // not able to parse } sampleStartMillis = ReadInt64(); if (IsLogLevelDebugEnabled) { var sampleStart = new DateTime( (sampleStartMillis * TimeSpan.TicksPerMillisecond) + TimeConstants.UnixEpochInTicks).ToLocalTime(); Log.Debug( "Parsing thread samples captured at {date} {time}", sampleStart.ToLongDateString(), sampleStart.ToLongTimeString()); } } else if (operationCode == OpCodes.StartSample) { var managedId = ReadInt(); var nativeId = ReadInt(); var threadName = ReadString(); var traceIdHigh = ReadInt64(); var traceIdLow = ReadInt64(); var spanId = ReadInt64(); var threadIndex = batchThreadIndex++; var code = ReadShort(); if (code == 0) { // Empty stack, skip this sample. continue; } var threadSample = new ThreadSample { Timestamp = new ThreadSample.Time(sampleStartMillis), TraceIdHigh = traceIdHigh, TraceIdLow = traceIdLow, SpanId = spanId, ManagedId = managedId, NativeId = nativeId, ThreadName = threadName, ThreadIndex = threadIndex }; while (code != 0) { string value; if (code < 0) { value = ReadString(); _codes[-code] = value; } else { value = _codes[code]; } if (value != null) { // we are replacing Datadog.Trace namespace to avoid conflicts while upstream sync var replacedValue = value.Replace("Datadog.Trace.", "SignalFx.Tracing."); threadSample.Frames.Add(replacedValue); } code = ReadShort(); } if (threadName == ThreadSampler.BackgroundThreadName) { // TODO Splunk: add configuration option to include the sampler thread. By default remove it. continue; } samples.Add(threadSample); } else if (operationCode == OpCodes.EndBatch) { // end batch, nothing here } else if (operationCode == OpCodes.BatchStats) { var microsSuspended = ReadInt(); var numThreads = ReadInt(); var totalFrames = ReadInt(); var numCacheMisses = ReadInt(); if (IsLogLevelDebugEnabled) { Log.Debug( "CLR was suspended for {microsSuspended} microseconds to collect a thread sample batch: threads={numThreads} frames={totalFrames} misses={numCacheMisses}", new object[] { microsSuspended, numThreads, totalFrames, numCacheMisses }); } } else { _position = _length + 1; if (IsLogLevelDebugEnabled) { Log.Debug("Not expected operation code while parsing thread stack trace: '{0}'. Operation will be ignored.", operationCode); } } } return(samples); }