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
178 changes: 176 additions & 2 deletions src/actions/publish.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,35 @@ import {
getDataSource,
getProfileNameForStorage,
getUrlPredictor,
getGlobalTrackOrder,
getHiddenGlobalTracks,
getHiddenLocalTracksByPid,
getLocalTrackOrderByPid,
getTabFilter,
} from 'firefox-profiler/selectors/url-state';
import {
getProfile,
getZeroAt,
getCommittedRange,
getGlobalTracks,
getLocalTracksByPid,
} from 'firefox-profiler/selectors/profile';
import { viewProfile } from './receive-profile';
import { ensureExists } from 'firefox-profiler/utils/types';
import { extractProfileTokenFromJwt } from 'firefox-profiler/utils/jwt';
import { withHistoryReplaceStateSync } from 'firefox-profiler/app-logic/url-handling';
import { persistUploadedProfileInformationToDb } from 'firefox-profiler/app-logic/uploaded-profiles-db';
import {
computeGlobalTracks,
computeLocalTracksByPid,
computeOldTrackIndexToNewTrackIndexMap,
computeHiddenTracksAfterSanitization,
computeTrackOrderAfterSanitization,
} from 'firefox-profiler/profile-logic/tracks';
import {
computeInnerWindowIDToTabMap,
computeTabToThreadIndexesMap,
} from 'firefox-profiler/profile-logic/profile-data';

import type {
Action,
Expand All @@ -36,6 +54,11 @@ import type {
StartEndRange,
State,
Profile,
Pid,
RawCounter,
CounterIndex,
ThreadIndex,
TrackIndex,
ProfileIndexTranslationMaps,
} from 'firefox-profiler/types';
import { compress } from 'firefox-profiler/utils/gz';
Expand Down Expand Up @@ -232,6 +255,47 @@ async function persistJustUploadedProfileInformationToDb(
}
}

/**
* Map each old CounterIndex to its new CounterIndex across a sanitization
* step. Sanitization removes counters whose parent thread is removed; the
* survivors keep their relative order. Each counter is identified by
* (pid, category, name, mainThreadIndex), with the old-side mainThreadIndex
* normalized through `oldThreadIndexToNew`.
*/
function _computeOldCounterIndexToNew(
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This is not an action, can you please move it to profile-logic/profile-data.ts.

oldCounters: RawCounter[] | null | undefined,
newCounters: RawCounter[] | null | undefined,
oldThreadIndexToNew: Map<ThreadIndex, ThreadIndex>
): Map<CounterIndex, CounterIndex> {
const result = new Map<CounterIndex, CounterIndex>();
if (!oldCounters || !newCounters) {
return result;
}
const newKeyToIndex = new Map<string, CounterIndex>();
for (let i = 0; i < newCounters.length; i++) {
const c = newCounters[i];
newKeyToIndex.set(
`${c.pid}|${c.category}|${c.name}|${c.mainThreadIndex}`,
i
);
}
for (let i = 0; i < oldCounters.length; i++) {
const c = oldCounters[i];
const newMainThreadIndex = oldThreadIndexToNew.get(c.mainThreadIndex);
if (newMainThreadIndex === undefined) {
// The counter's parent thread is gone in the new profile, so the
// counter is gone too.
continue;
}
const key = `${c.pid}|${c.category}|${c.name}|${newMainThreadIndex}`;
const newIndex = newKeyToIndex.get(key);
if (newIndex !== undefined) {
result.set(i, newIndex);
}
}
return result;
}

export type ProfileEncodingResult =
| {
type: 'SUCCESS';
Expand Down Expand Up @@ -413,6 +477,99 @@ export function attemptToPublish(
if (removeProfileInformation) {
const { committedRanges, translationMaps, profile } =
sanitizedInformation;

// When sanitization re-indexed tracks, translate the URL state's
// track-index references through old → new track-index space so the
// user's choices for tracks that survived sanitization stay attached
// to the right tracks.
let remappedHiddenGlobalTracks: Set<TrackIndex> | null = null;
let remappedGlobalTrackOrder: TrackIndex[] | null = null;
let remappedHiddenLocalTracksByPid: Map<Pid, Set<TrackIndex>> | null =
null;
let remappedLocalTrackOrderByPid: Map<Pid, TrackIndex[]> | null = null;

const oldThreadIndexToNew = translationMaps?.oldThreadIndexToNew;
if (oldThreadIndexToNew) {
const oldProfile = getProfile(prePublishedState);
const oldCounterIndexToNew = _computeOldCounterIndexToNew(
oldProfile.counters,
profile.counters,
oldThreadIndexToNew
);

// The selector-derived map keys ThreadIndex values from the
// prePublishedState; rebuild it against the sanitized profile.
const newTabToThreadIndexesMap = computeTabToThreadIndexesMap(
profile.threads,
computeInnerWindowIDToTabMap(profile.pages)
);
const tabFilter = getTabFilter(prePublishedState);
const newGlobalTracks = computeGlobalTracks(
profile,
tabFilter,
newTabToThreadIndexesMap
);
const newLocalTracksByPid = computeLocalTracksByPid(
profile,
newGlobalTracks
);

const oldGlobalTracks = getGlobalTracks(prePublishedState);
const oldLocalTracksByPid = getLocalTracksByPid(prePublishedState);

const globalOldToNew = computeOldTrackIndexToNewTrackIndexMap({
oldTracks: oldGlobalTracks,
newTracks: newGlobalTracks,
oldThreadIndexToNew,
oldCounterIndexToNew,
});

remappedHiddenGlobalTracks = computeHiddenTracksAfterSanitization({
oldHiddenTracks: getHiddenGlobalTracks(prePublishedState),
oldTrackIndexToNewTrackIndex: globalOldToNew,
});
remappedGlobalTrackOrder = computeTrackOrderAfterSanitization({
oldTrackOrder: getGlobalTrackOrder(prePublishedState),
oldTrackIndexToNewTrackIndex: globalOldToNew,
});

remappedHiddenLocalTracksByPid = new Map();
remappedLocalTrackOrderByPid = new Map();
const oldHiddenLocalByPid =
getHiddenLocalTracksByPid(prePublishedState);
const oldLocalOrderByPid = getLocalTrackOrderByPid(prePublishedState);

for (const [pid, newLocalTracks] of newLocalTracksByPid) {
const oldLocalTracks = oldLocalTracksByPid.get(pid) ?? [];
const localOldToNew = computeOldTrackIndexToNewTrackIndexMap({
oldTracks: oldLocalTracks,
newTracks: newLocalTracks,
oldThreadIndexToNew,
oldCounterIndexToNew,
});
const oldHiddenForPid = oldHiddenLocalByPid.get(pid);
if (oldHiddenForPid !== undefined) {
remappedHiddenLocalTracksByPid.set(
pid,
computeHiddenTracksAfterSanitization({
oldHiddenTracks: oldHiddenForPid,
oldTrackIndexToNewTrackIndex: localOldToNew,
})
);
}
const oldOrderForPid = oldLocalOrderByPid.get(pid);
if (oldOrderForPid !== undefined) {
remappedLocalTrackOrderByPid.set(
pid,
computeTrackOrderAfterSanitization({
oldTrackOrder: oldOrderForPid,
oldTrackIndexToNewTrackIndex: localOldToNew,
})
);
}
}
}

// Hide the old UI gracefully.
await dispatch(hideStaleProfile());

Expand All @@ -423,7 +580,11 @@ export function attemptToPublish(
committedRanges,
translationMaps,
profileName,
prePublishedState
prePublishedState,
remappedHiddenGlobalTracks,
remappedGlobalTrackOrder,
remappedHiddenLocalTracksByPid,
remappedLocalTrackOrderByPid
)
);

Expand Down Expand Up @@ -504,13 +665,22 @@ export function resetUploadState(): Action {
/**
* Report to the UrlState that the profile was sanitized. This will re-map any stored
* indexes or information that has been sanitized away.
*
* The four track-index payload fields carry URL state translated into the
* post-sanitization track-index space when sanitization re-indexed tracks
* (translationMaps.oldThreadIndexToNew is non-null). They are null when no
* remap is needed; in that case the existing reducer state is left untouched.
*/
export function profileSanitized(
hash: string,
committedRanges: StartEndRange[] | null,
translationMaps: ProfileIndexTranslationMaps | null,
profileName: string,
prePublishedState: State | null
prePublishedState: State | null,
hiddenGlobalTracks: Set<TrackIndex> | null = null,
globalTrackOrder: TrackIndex[] | null = null,
hiddenLocalTracksByPid: Map<Pid, Set<TrackIndex>> | null = null,
localTrackOrderByPid: Map<Pid, TrackIndex[]> | null = null
): Action {
return {
type: 'SANITIZED_PROFILE_PUBLISHED',
Expand All @@ -519,6 +689,10 @@ export function profileSanitized(
translationMaps,
profileName,
prePublishedState,
hiddenGlobalTracks,
globalTrackOrder,
hiddenLocalTracksByPid,
localTrackOrderByPid,
};
}

Expand Down
17 changes: 17 additions & 0 deletions src/profile-logic/profile-data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4469,6 +4469,23 @@ export function determineTimelineType(profile: Profile): TimelineType {
return 'cpu-category';
}

/**
* Compute an innerWindowID → tabID lookup from a profile's pages list.
* Returns null when the profile has no pages.
*/
export function computeInnerWindowIDToTabMap(
pages: PageList | null | undefined
): Map<InnerWindowID, TabID> | null {
if (!pages) {
return null;
}
const innerWindowIDToTabMap = new Map<InnerWindowID, TabID>();
for (const page of pages) {
innerWindowIDToTabMap.set(page.innerWindowID, page.tabID);
}
return innerWindowIDToTabMap;
}

/**
* Compute a map of tab to thread indexes map. This is useful for learning which
* threads are involved for tabs. This is mainly used for the tab selector on
Expand Down
Loading
Loading