Skip to content
Merged
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
1 change: 1 addition & 0 deletions profiler-cli/schemas.txt
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,7 @@ profiler-cli thread markers --json
type: "thread-markers",
threadHandle, friendlyThreadName,
totalMarkerCount, filteredMarkerCount,
fullRangeMarkerCount?,
byType: [{
markerName, count, isInterval,
durationStats?: { min, max, avg, median, p95, p99 },
Expand Down
8 changes: 7 additions & 1 deletion profiler-cli/src/formatters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1124,8 +1124,14 @@ export function formatThreadMarkersResult(
? ` (filtered from ${result.totalMarkerCount})`
: '';

// When zoomed, show the in-view count against this thread's full-range total.
const zoomSuffix =
result.fullRangeMarkerCount !== undefined
? ` in view (of ${result.fullRangeMarkerCount} in the full range)`
: '';

lines.push(
`Markers in thread ${result.threadHandle} (${result.friendlyThreadName}) — ${result.filteredMarkerCount} markers${filterSuffix}`
`Markers in thread ${result.threadHandle} (${result.friendlyThreadName}) — ${result.filteredMarkerCount} markers${filterSuffix}${zoomSuffix}`
);
lines.push('Legend: ✓ = has stack trace, ✗ = no stack trace\n');

Expand Down
21 changes: 21 additions & 0 deletions profiler-cli/src/test/unit/marker-formatting.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -148,3 +148,24 @@ describe('formatThreadMarkersResult flat list mode', function () {
expect(output).not.toContain('By Category');
});
});

describe('formatThreadMarkersResult zoom baseline', function () {
it('notes the full-range total when zoomed', function () {
const result = makeResult({
filteredMarkerCount: 3,
totalMarkerCount: 3,
fullRangeMarkerCount: 42,
});

const output = formatThreadMarkersResult(result);
expect(output).toContain('3 markers in view (of 42 in the full range)');
});

it('omits the full-range note when not zoomed', function () {
const result = makeResult({ filteredMarkerCount: 3, totalMarkerCount: 3 });

const output = formatThreadMarkersResult(result);
expect(output).not.toContain('in the full range');
expect(output).toContain('3 markers');
});
});
20 changes: 16 additions & 4 deletions src/profile-query/formatters/marker-info.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

import { getSelectedThreadIndexes } from 'firefox-profiler/selectors/url-state';
import {
getSelectedThreadIndexes,
getAllCommittedRanges,
} from 'firefox-profiler/selectors/url-state';
import {
getProfile,
getCategories,
Expand Down Expand Up @@ -702,12 +705,20 @@ export function collectThreadMarkers(
const markerSchemaByName = getMarkerSchemaByName(state);
const stringTable = getStringTable(state);

// Get marker indexes - use search-filtered if search is active, otherwise all markers
// Get marker indexes scoped to the committed (zoom) range. When a search is
// active we use the search-filtered set, which is itself built on top of the
// committed-range-filtered indexes, so both paths respect the current zoom.
const originalCount =
threadSelectors.getFullMarkerListIndexes(state).length;
threadSelectors.getCommittedRangeFilteredMarkerIndexes(state).length;
let filteredIndexes = searchString
? threadSelectors.getSearchFilteredMarkerIndexes(state)
: threadSelectors.getFullMarkerListIndexes(state);
: threadSelectors.getCommittedRangeFilteredMarkerIndexes(state);

// When zoomed, show this thread's marker count over the full time range.
const isZoomed = getAllCommittedRanges(state).length > 0;
const fullRangeMarkerCount = isZoomed
? threadSelectors.getFullMarkerListIndexes(state).length
: undefined;

// Apply all marker filters
filteredIndexes = applyMarkerFilters(
Expand Down Expand Up @@ -864,6 +875,7 @@ export function collectThreadMarkers(
friendlyThreadName,
totalMarkerCount: originalCount,
filteredMarkerCount: filteredIndexes.length,
fullRangeMarkerCount,
filters,
byType,
byCategory,
Expand Down
1 change: 1 addition & 0 deletions src/profile-query/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -570,6 +570,7 @@ export type ThreadMarkersResult = {
friendlyThreadName: string;
totalMarkerCount: number;
filteredMarkerCount: number;
fullRangeMarkerCount?: number;
filters?: {
searchString?: string;
minDuration?: number;
Expand Down
59 changes: 59 additions & 0 deletions src/test/unit/profile-query/profile-querier.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -694,4 +694,63 @@ describe('ProfileQuerier', function () {
expect(zoomed.networkActivity!.inFlightMs).toBeLessThan(fullInFlight);
});
});

describe('threadMarkers', function () {
function querierWithMarkers() {
const profile = getProfileWithMarkers([
['Alpha', 10, null, { type: 'tracing', category: 'Test' }],
['Beta', 20, null, { type: 'tracing', category: 'Test' }],
['Gamma', 30, null, { type: 'tracing', category: 'Test' }],
['Delta', 40, null, { type: 'tracing', category: 'Test' }],
]);
const store = storeWithProfile(profile);
const rootRange = getProfileRootRange(store.getState());
return { querier: new ProfileQuerier(store, rootRange), rootRange };
}

it('restricts the default marker list to the committed (zoom) range', async function () {
const { querier, rootRange } = querierWithMarkers();

const full = await querier.threadMarkers('t-0');
expect(full.totalMarkerCount).toBe(4);
expect(full.filteredMarkerCount).toBe(4);
// Not zoomed: no full-range baseline is reported.
expect(full.fullRangeMarkerCount).toBeUndefined();

// Zoom to a window that only contains the marker at 20ms.
const startName = querier._timestampManager.nameForTimestamp(
rootRange.start + 2
);
const endName = querier._timestampManager.nameForTimestamp(
rootRange.start + 18
);
await querier.pushViewRange(`${startName},${endName}`);

const zoomed = await querier.threadMarkers('t-0');
expect(zoomed.totalMarkerCount).toBe(1);
expect(zoomed.filteredMarkerCount).toBe(1);
// Zoomed: the whole-profile baseline is surfaced alongside the in-view count.
expect(zoomed.fullRangeMarkerCount).toBe(4);
const zoomedNames = zoomed.byType.map((t) => t.markerName);
expect(zoomedNames).toContain('Beta');
expect(zoomedNames).not.toContain('Alpha');
expect(zoomedNames).not.toContain('Delta');
});

it('restricts the --list output to the committed (zoom) range', async function () {
const { querier, rootRange } = querierWithMarkers();

const startName = querier._timestampManager.nameForTimestamp(
rootRange.start + 2
);
const endName = querier._timestampManager.nameForTimestamp(
rootRange.start + 18
);
await querier.pushViewRange(`${startName},${endName}`);

const zoomed = await querier.threadMarkers('t-0', { list: true });
const listedNames = zoomed.flatMarkers!.map((m) => m.name);
expect(listedNames).toEqual(['Beta']);
});
});
});
Loading