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
1 change: 1 addition & 0 deletions locales/en-US/app.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -724,6 +724,7 @@ MenuButtons--publish--renderCheckbox-label-preference = Include preference value
MenuButtons--publish--renderCheckbox-label-private-browsing = Include the data from private browsing windows
MenuButtons--publish--renderCheckbox-label-private-browsing-warning-image =
.title = This profile contains private browsing data
MenuButtons--publish--renderCheckbox-label-argument-values = Include function argument values
MenuButtons--publish--reupload-performance-profile = Re-upload Performance Profile
MenuButtons--publish--share-performance-profile = Share Performance Profile
MenuButtons--publish--info-description = Upload your profile and make it accessible to anyone with the link.
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@
"common-tags": "^1.8.2",
"copy-to-clipboard": "^3.3.3",
"core-js": "^3.48.0",
"devtools-reps": "^0.27.4",
"devtools-reps": "^0.27.6",
"escape-string-regexp": "^4.0.0",
"gecko-profiler-demangle": "^0.4.0",
"idb": "^8.0.3",
Expand Down
10 changes: 10 additions & 0 deletions src/components/app/MenuButtons/Publish.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
getProfileRootRange,
getHasPreferenceMarkers,
getContainsPrivateBrowsingInformation,
getHasArgumentValues,
} from 'firefox-profiler/selectors/profile';
import {
getAbortFunction,
Expand Down Expand Up @@ -58,6 +59,7 @@ type StateProps = {
readonly rootRange: StartEndRange;
readonly shouldShowPreferenceOption: boolean;
readonly profileContainsPrivateBrowsingInformation: boolean;
readonly profileHasArgumentValues: boolean;
readonly checkedSharingOptions: CheckedSharingOptions;
readonly sanitizedProfileEncodingState: SanitizedProfileEncodingState;
readonly downloadFileName: string;
Expand Down Expand Up @@ -123,6 +125,7 @@ class PublishPanelImpl extends React.PureComponent<PublishProps, {}> {
const {
shouldShowPreferenceOption,
profileContainsPrivateBrowsingInformation,
profileHasArgumentValues,
sanitizedProfileEncodingState,
downloadFileName,
shouldSanitizeByDefault,
Expand Down Expand Up @@ -210,6 +213,12 @@ class PublishPanelImpl extends React.PureComponent<PublishProps, {}> {
</Localized>
)
: null}
{profileHasArgumentValues
? this._renderCheckbox(
'includeArgumentValues',
'MenuButtons--publish--renderCheckbox-label-argument-values'
)
: null}
</div>
{sanitizedProfileEncodingState.phase === 'ERROR' ? (
<div className="photon-message-bar photon-message-bar-error photon-message-bar-inner-content">
Expand Down Expand Up @@ -361,6 +370,7 @@ export const PublishPanel = explicitConnect<
shouldShowPreferenceOption: getHasPreferenceMarkers(state),
profileContainsPrivateBrowsingInformation:
getContainsPrivateBrowsingInformation(state),
profileHasArgumentValues: getHasArgumentValues(state),
checkedSharingOptions: getCheckedSharingOptions(state),
downloadFileName: getFilenameString(state),
sanitizedProfileEncodingState: getSanitizedProfileEncodingState(state),
Expand Down
40 changes: 37 additions & 3 deletions src/profile-logic/profile-data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,8 @@ import type {
} from 'firefox-profiler/types';
import { SelectedState, ResourceType } from 'firefox-profiler/types';
import type { CallNodeInfo, SuffixOrderIndex } from './call-node-info';
import { bytesToBase64 } from 'firefox-profiler/utils/base64';
import { ValueSummaryReader } from 'devtools-reps';

/**
* Various helpers for dealing with the profile as a data structure.
Expand Down Expand Up @@ -2263,14 +2265,46 @@ export function filterCounterSamplesToRange(
number: samples.number
? samples.number.slice(beginSampleIndex, endSampleIndex)
: undefined,
argumentValues: samples.argumentValues
? samples.argumentValues.slice(beginSampleIndex, endSampleIndex)
: undefined,
};

return newCounter;
}

/**
* Filter a traced values buffer to only include entries that are referenced
* by the given argument values array. This is used during sanitization when
* filtering to a committed time range.
*/
export function filterTracedValuesBufferToEntries(
tracedValuesBuffer: ArrayBuffer,
thread: RawThread
): RawThread {
if (
!thread.samples.argumentValues ||
!thread.tracedValuesBuffer ||
!thread.tracedObjectShapes
) {
throw new Error(
'filterTracedValuesBufferToEntries should only be called with JS Execution Tracer profiles'
);
}

const newThread: RawThread = { ...thread };
const argumentValues: Array<number | null> = [
...thread.samples.argumentValues,
];

const filtered = ValueSummaryReader.filterValuesBufferToEntries(
tracedValuesBuffer,
argumentValues
);

newThread.tracedValuesBuffer = bytesToBase64(filtered.valuesBuffer);
newThread.samples.argumentValues = filtered.entryIndices;

return newThread;
}

/**
* Process the samples in the counter.
*/
Expand Down
22 changes: 20 additions & 2 deletions src/profile-logic/sanitize.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import { getSchemaFromMarker } from './marker-schema';
import {
filterRawThreadSamplesToRange,
filterCounterSamplesToRange,
filterTracedValuesBufferToEntries,
} from './profile-data';
import type {
Profile,
Expand Down Expand Up @@ -58,6 +59,7 @@ const PRIVATE_BROWSING_STACK = 1;
export function sanitizePII(
profile: Profile,
derivedMarkerInfoForAllThreads: DerivedMarkerInfo[],
tracedValuesBuffers: Array<ArrayBuffer | undefined>,
maybePIIToBeRemoved: RemoveProfileInformation | null,
markerSchemaByName: MarkerSchemaByName
): SanitizeProfileResult {
Expand Down Expand Up @@ -306,6 +308,7 @@ export function sanitizePII(
thread,
stringTable,
derivedMarkerInfoForAllThreads[threadIndex],
tracedValuesBuffers[threadIndex],
threadIndex,
PIIToBeRemoved,
windowIdFromPrivateBrowsing,
Expand Down Expand Up @@ -420,6 +423,7 @@ function sanitizeThreadPII(
thread: RawThread,
stringTable: StringTable,
derivedMarkerInfo: DerivedMarkerInfo,
tracedValuesBuffer: ArrayBuffer | undefined,
threadIndex: number,
PIIToBeRemoved: RemoveProfileInformation,
windowIdFromPrivateBrowsing: Set<InnerWindowID>,
Expand Down Expand Up @@ -592,8 +596,22 @@ function sanitizeThreadPII(
delete newThread['eTLD+1'];
}

delete newThread.tracedValuesBuffer;
delete newThread.tracedObjectShapes;
if (
newThread.samples.argumentValues &&
tracedValuesBuffer &&
newThread.tracedObjectShapes &&
!PIIToBeRemoved.shouldRemoveArgumentValues
) {
newThread = filterTracedValuesBufferToEntries(
tracedValuesBuffer,
newThread
);
} else {
delete newThread.tracedValuesBuffer;
delete newThread.tracedObjectShapes;
newThread.samples = { ...newThread.samples };
delete newThread.samples.argumentValues;
}

const { samples } = newThread;
if (stackFlags !== null && windowIdFromPrivateBrowsing.size > 0) {
Expand Down
3 changes: 3 additions & 0 deletions src/reducers/publish.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ function _getSanitizingSharingOptions(): CheckedSharingOptions {
includeExtension: false,
includePreferenceValues: false,
includePrivateBrowsingData: false,
includeArgumentValues: false,
};
}

Expand All @@ -39,6 +40,8 @@ function _getMostlyNonSanitizingSharingOptions(): CheckedSharingOptions {
includePreferenceValues: true,
// We always want to sanitize the private browsing data by default
includePrivateBrowsingData: false,
// We always want to sanitize the argument values by default since they may contain PII
includeArgumentValues: false,
};
}

Expand Down
7 changes: 7 additions & 0 deletions src/selectors/profile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -912,6 +912,13 @@ export const getContainsPrivateBrowsingInformation: Selector<boolean> =
return hasPrivateThreads;
});

// Gets whether this profile contains argument values from JS execution tracing.
export const getHasArgumentValues: Selector<boolean> = createSelector(
getThreads,
(threads) =>
threads.some((thread) => thread.samples.argumentValues !== undefined)
);

/**
* Returns the TIDs of the threads that are profiled.
*/
Expand Down
38 changes: 33 additions & 5 deletions src/selectors/publish.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
getLocalTracksByPid,
getHasPreferenceMarkers,
getContainsPrivateBrowsingInformation,
getHasArgumentValues,
getThreads,
getMarkerSchemaByName,
} from './profile';
Expand Down Expand Up @@ -80,6 +81,7 @@ export const getRemoveProfileInformation: Selector<RemoveProfileInformation | nu
getLocalTracksByPid,
getHasPreferenceMarkers,
getContainsPrivateBrowsingInformation,
getHasArgumentValues,
(
checkedSharingOptions,
profile,
Expand All @@ -89,14 +91,15 @@ export const getRemoveProfileInformation: Selector<RemoveProfileInformation | nu
globalTracks,
localTracksByPid,
hasPreferenceMarkers,
containsPrivateBrowsingInformation
containsPrivateBrowsingInformation,
hasArgumentValues
) => {
let isIncludingEverything = true;
for (const [prop, value] of Object.entries(checkedSharingOptions)) {
// Do not include preference values or private browsing checkboxes if
// they're hidden. Even though `includePreferenceValues` is not taken
// into account, it is false, if the profile updateChannel is not
// nightly or custom build.
// Do not include preference values, private browsing, or argument
// values checkboxes if they're hidden. Even though
// `includePreferenceValues` is not taken into account, it is false, if
// the profile updateChannel is not nightly or custom build.
if (prop === 'includePreferenceValues' && !hasPreferenceMarkers) {
continue;
}
Expand All @@ -106,6 +109,9 @@ export const getRemoveProfileInformation: Selector<RemoveProfileInformation | nu
) {
continue;
}
if (prop === 'includeArgumentValues' && !hasArgumentValues) {
continue;
}
isIncludingEverything = isIncludingEverything && value;
}
if (isIncludingEverything) {
Expand Down Expand Up @@ -177,6 +183,8 @@ export const getRemoveProfileInformation: Selector<RemoveProfileInformation | nu
!checkedSharingOptions.includePreferenceValues,
shouldRemovePrivateBrowsingData:
!checkedSharingOptions.includePrivateBrowsingData,
shouldRemoveArgumentValues:
!checkedSharingOptions.includeArgumentValues,
};
}
);
Expand All @@ -202,6 +210,25 @@ function getDerivedMarkerInfoForAllThreads(state: State): DerivedMarkerInfo[] {
return _derivedMarkerInfo;
}

/**
* The traced values buffers are needed for profile sanitization when filtering
* argument values to a time range. Similar memoization approach as above.
*/
let _threadsForBuffers: any = null;
let _tracedValuesBuffers: Array<ArrayBuffer | undefined> | null = null;
function getTracedValuesBuffersForAllThreads(
state: State
): Array<ArrayBuffer | undefined> {
const threads = getThreads(state);
if (_threadsForBuffers !== threads || _tracedValuesBuffers === null) {
_threadsForBuffers = threads;
_tracedValuesBuffers = threads.map((_: any, threadIndex: ThreadIndex) =>
getThreadSelectors(threadIndex).getTracedValuesBuffer(state)
);
}
return _tracedValuesBuffers;
}

/**
* Run the profile sanitization step, and also get information about how any
* UrlState needs to be updated, with things like mapping thread indexes,
Expand All @@ -211,6 +238,7 @@ export const getSanitizedProfile: Selector<SanitizeProfileResult> =
createSelector(
getProfile,
getDerivedMarkerInfoForAllThreads,
getTracedValuesBuffersForAllThreads,
getRemoveProfileInformation,
getMarkerSchemaByName,
sanitizePII
Expand Down
2 changes: 2 additions & 0 deletions src/test/store/publish.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ describe('getCheckedSharingOptions', function () {
describe('default filtering by channel', function () {
const isFiltering = {
includeExtension: false,
includeArgumentValues: false,
includeFullTimeRange: false,
includeHiddenThreads: false,
includeAllTabs: false,
Expand All @@ -83,6 +84,7 @@ describe('getCheckedSharingOptions', function () {
};
const isNotFiltering = {
includeExtension: true,
includeArgumentValues: false,
includeFullTimeRange: true,
includeHiddenThreads: true,
includeAllTabs: true,
Expand Down
4 changes: 4 additions & 0 deletions src/test/unit/sanitize.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ describe('sanitizePII', function () {
shouldRemoveExtensions: false,
shouldRemovePreferenceValues: false,
shouldRemovePrivateBrowsingData: false,
shouldRemoveArgumentValues: false,
};

const PIIToRemove: RemoveProfileInformation = {
Expand Down Expand Up @@ -132,9 +133,12 @@ describe('sanitizePII', function () {
},
};

const tracedValuesBuffers = originalProfile.threads.map(() => undefined);

const sanitizedProfile = sanitizePII(
originalProfile,
derivedMarkerInfoForAllThreads,
tracedValuesBuffers,
PIIToRemove,
markerSchemaByName
).profile;
Expand Down
9 changes: 9 additions & 0 deletions src/types/@types/devtools-reps/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,20 @@ declare module 'devtools-reps' {
export function maybeEscapePropertyName(name: string): string;
export function getGripPreviewItems(grip: any): any[];

type FilterValuesBufferResult = {
valuesBuffer: ArrayBuffer;
entryIndices: Array<number | null>;
};

export const ValueSummaryReader: {
getArgumentSummaries: (
valuesBuffer: ArrayBuffer,
shapes: Array<string[] | null>,
valuesBufferIndex: number
) => Array<object> | string;
filterValuesBufferToEntries: (
srcBuffer: ArrayBuffer,
entryIndices: Array<number | null>
) => FilterValuesBufferResult;
};
}
1 change: 1 addition & 0 deletions src/types/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,7 @@ export type CheckedSharingOptions = {
includeExtension: boolean;
includePreferenceValues: boolean;
includePrivateBrowsingData: boolean;
includeArgumentValues: boolean;
};

// This type is used when selecting tracks in the timeline. Ctrl and Meta are
Expand Down
2 changes: 2 additions & 0 deletions src/types/profile-derived.ts
Original file line number Diff line number Diff line change
Expand Up @@ -676,6 +676,8 @@ export type RemoveProfileInformation = {
readonly shouldRemovePreferenceValues: boolean;
// Remove the private browsing data if it's true.
readonly shouldRemovePrivateBrowsingData: boolean;
// Remove the argument values captured by the JS execution tracer if it's true.
readonly shouldRemoveArgumentValues: boolean;
};

/**
Expand Down
1 change: 0 additions & 1 deletion src/types/profile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -513,7 +513,6 @@ export type RawCounterSamplesTable = {
number?: number[];
// The count of the data, for instance for memory this would be bytes.
count: number[];
argumentValues?: Array<number | null>;
length: number;
};

Expand Down
Loading
Loading