Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 13 additions & 2 deletions ts/src/time-profiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -168,10 +168,12 @@ export function stop(
}

const profile = gProfiler.stop(restart);

if (restart) {
// Restart the profiler *before* serializing so the cost of serialization is
// captured in the next profile. handleStopRestart() does not dispose of the
// source mapper, so the source-map resolution below is unaffected.
handleStopRestart();
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We definitely want to – at least for the restart case – to stop/start the profiler before we serialize, as then the serialization work itself is captured in the next profile; we like to be honest like that and let the users observe its cost :-) Arguably, for the plain stop case it doesn't matter. I guess maybe we'll need to blow up this nice encapsulation of handleStop[No]Restart() here and have an early stop-start for the restart case (which should not dispose of source maps, right?) and a late stop for for no-restart case.

} else {
handleStopNoRestart();
}

const serializedProfile = serializeTimeProfile(
Expand All @@ -182,6 +184,15 @@ export function stop(
generateLabels,
lowCardinalityLabels,
);

if (!restart) {
// Tear down *after* serializing: handleStopNoRestart() clears gSourceMapper
// (and disposes gProfiler), so serializing afterwards would drop source-map
// resolution and leave transpiled frames pointing at the generated files
// instead of the original sources.
handleStopNoRestart();
}

return serializedProfile;
}

Expand Down
46 changes: 46 additions & 0 deletions ts/test/test-time-profiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ import * as sinon from 'sinon';
import {time, getNativeThreadId} from '../src';
import {profileV2, stopV2} from '../src/time-profiler';
import * as v8TimeProfiler from '../src/time-profiler-bindings';
import * as profileSerializer from '../src/profile-serializer';
import {SourceMapper} from '../src/sourcemapper/sourcemapper';
import {timeProfile, v8TimeProfile} from './profiles-for-tests';
import {hrtime} from 'process';
import {Label, Profile} from 'pprof-format';
Expand Down Expand Up @@ -495,6 +497,50 @@ describe('Time Profiler', () => {
sinon.assert.notCalled(timeProfilerStub.start);
sinon.assert.calledOnce(timeProfilerStub.stop);
});

it('should serialize with the source mapper still set when stopping', () => {
// Regression test: stop() used to tear down the profiler state (via
// handleStopNoRestart, which clears gSourceMapper) *before* serializing,
// so the source mapper passed to start() was dropped and transpiled
// frames were left pointing at the generated files instead of the
// original sources. The third argument to serializeTimeProfile is the
// source mapper; it must still be the one passed to start().
const sourceMapper = {} as unknown as SourceMapper;
const serializeStub = sinon
.stub(profileSerializer, 'serializeTimeProfile')
.returns(timeProfile);
try {
// no-restart path: handleStopNoRestart() clears gSourceMapper, so it
// must run after serialization.
time.start({
intervalMicros: PROFILE_OPTIONS.intervalMicros,
sourceMapper,
});
time.stop();
sinon.assert.calledOnce(serializeStub);
assert.strictEqual(
serializeStub.getCall(0).args[2],
sourceMapper,
'source mapper dropped on stop()',
);

// restart path: the source mapper must be preserved here too.
serializeStub.resetHistory();
time.start({
intervalMicros: PROFILE_OPTIONS.intervalMicros,
sourceMapper,
});
time.stop(true);
assert.strictEqual(
serializeStub.getCall(0).args[2],
sourceMapper,
'source mapper dropped on stop(true)',
);
time.stop(); // finalize: dispose the restarted profiler
} finally {
serializeStub.restore();
}
});
});

describe('profileV2', () => {
Expand Down