Skip to content

jfr2pprof: frame.format is parsed but never applied — Function.Name loses the method name #105

Description

@jbachorik

Summary

jfr2pprof's frame.format mapping config (e.g. "{class}.{method}") is loaded by MappingLoader into a FrameFormat, but the conversion path never renders through it. The result: every pprof Function.Name is just the bare class name, and the method name is silently dropped into Function.SystemName, which google/pprof (and most pprof consumers) don't use for symbolization/folded-stack display.

Root cause

PprofBuilder.internStack():

FrameExtractor.FrameData data = FrameExtractor.extractData(frameElement);
if (data == null) continue;

long fnId =
    internFunction(
        data.className(), data.methodName(), data.lineNumber() < 0 ? 0 : data.lineNumber());

calls internFunction(className, methodName, ...) directly with the raw extracted class/method strings. internFunction writes className into Function.Name and methodName into Function.SystemName:

public long internFunction(String className, String methodName, long startLine) {
  ...
  functionEntries.add(
      new long[] {
        id, internString(className), internString(methodName), internString(""), startLine
      });
  ...
}

FrameExtractor.extract(Object frame, FrameFormat fmt) — the method that actually calls fmt.render(...) to combine class+method per the configured format — exists but is never called anywhere in jfr2pprof's main source. It's dead code. The FrameFormat fmt parameter threaded into internStack(Object frames, FrameFormat fmt) is effectively unused.

Impact

Every frame from the same class collapses into a single pprof Function/node, regardless of which method it's actually in. Verified with pprof -raw on a real conversion:

30: 0x0 M=1 Workload$CpuBurner :29:0 s=29(spin)
31: 0x0 M=1 Workload$CpuBurner :22:0 s=22(run)

Both Workload$CpuBurner.run and Workload$CpuBurner.spin render as the same Function.Name (Workload$CpuBurner); the method landed in SystemName (s=...) instead. go tool pprof -top then folds both into one node.

This breaks any expected_profile.json-style assertion (used throughout DataDog/prof-correctness, and presumably any other consumer) that matches a folded stack string like ClassA.methodA;ClassB.methodB — the method-qualified regex can never match, for any mapping config, on any platform. It is not specific to a particular JFR event schema.

Suggested fix

In PprofBuilder.internStack, render the frame through the configured FrameFormat before interning it as the function's display name, e.g.:

String renderedName = fmt.render(data.className(), data.methodName(), data.lineNumber());
long fnId = internFunction(renderedName, data.methodName(), data.lineNumber() < 0 ? 0 : data.lineNumber());

(keeping raw methodName in SystemName as before). Discovered while validating datadog.yaml/the java-profiler prof-correctness scenario (PROF-15289/PROF-15290) — the mapping config and event data were confirmed correct; this is purely a converter-side gap.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions