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
2 changes: 1 addition & 1 deletion docs-developer/CHANGELOG-formats.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ Note that this is not an exhaustive list. Processed profile format upgraders can

### Version 72

Marker schema fields can now include a `containsPII` array describing the categories of privacy-sensitive data they contain. Profile sanitization uses these categories instead of identifying privacy-sensitive fields from the marker type.
Marker schema fields can now include a `containsPII` array describing the categories of privacy-sensitive data they contain. Profile sanitization uses these categories instead of identifying privacy-sensitive fields from the marker type. Extension-related text markers are converted to structured payloads so their extension IDs can be annotated separately.

### Version 71

Expand Down
22 changes: 2 additions & 20 deletions src/profile-logic/marker-data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1457,18 +1457,6 @@ export function groupScreenshotsById(
return idToScreenshotMarkers;
}

function _removeExtensionId(markerName: string, text: string): string {
if (['ExtensionParent', 'ExtensionChild'].includes(markerName)) {
return text.replace(/^.*, (api_(call|event): )/, '$1');
}

if (markerName === 'Extension Suspend') {
return text.replace(/ by .*$/, '');
}

return text;
}

function _shouldSanitizePIICategory(
category: MarkerSchemaPIICategory,
PIIToBeRemoved: RemoveProfileInformation
Expand Down Expand Up @@ -1517,7 +1505,6 @@ function _updateMarkerPayloadField(
/** Apply a marker schema's PII rules to its payload. */
export function sanitizeMarkerFromSchema(
markerSchema: MarkerSchema,
markerName: string,
markerPayload: MarkerPayload,
stringTable: StringTable,
PIIToBeRemoved: RemoveProfileInformation
Expand Down Expand Up @@ -1574,13 +1561,8 @@ export function sanitizeMarkerFromSchema(
break;
case 'extension-id':
if (hasField) {
markerPayload = _updateMarkerPayloadField(
markerPayload,
key,
isStringIndex,
stringTable,
(text) => _removeExtensionId(markerName, text)
);
markerPayload = { ...markerPayload };
delete (markerPayload as any)[key];
}
break;
case 'preference-value':
Expand Down
60 changes: 59 additions & 1 deletion src/profile-logic/marker-schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ const markerSchemaPIICategoriesBySchemaName = new Map<
['isPrivateBrowsing', ['private-browsing']],
]),
],
['Text', new Map([['name', ['url', 'extension-id']]])],
['Text', new Map([['name', ['url']]])],
['PreferenceRead', new Map([['prefValue', ['preference-value']]])],
]);

Expand Down Expand Up @@ -79,6 +79,64 @@ export function addPIICategoriesToMarkerSchemas(
return markerSchemas.map(addPIICategoriesToMarkerSchema);
}

const extensionApiMarkerSchemaFields: MarkerSchemaField[] = [
{
key: 'extensionId',
label: 'Extension ID',
format: 'string',
containsPII: ['extension-id'],
},
{
key: 'name',
label: 'Details',
format: 'string',
containsPII: ['url'],
},
];

export const extensionMarkerSchemas: MarkerSchema[] = [

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.

Similar to the comment I added in the types, we can reduce them into a single schema.

{
name: 'ExtensionParent',
tableLabel:
"{marker.data.extensionId}{marker.data.extensionId ? ', ' : ''}{marker.data.name}",
chartLabel:
"{marker.name} — {marker.data.extensionId}{marker.data.extensionId ? ', ' : ''}{marker.data.name}",

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.

Let's remove {marker.name} - from chartLabel. It doesn't add a lot of value there as we can already see the name easily on the left side of the marker chart.

display: ['marker-chart', 'marker-table'],
fields: extensionApiMarkerSchemaFields,
},
{
name: 'ExtensionChild',
tableLabel:
"{marker.data.extensionId}{marker.data.extensionId ? ', ' : ''}{marker.data.name}",
chartLabel:
"{marker.name} — {marker.data.extensionId}{marker.data.extensionId ? ', ' : ''}{marker.data.name}",
display: ['marker-chart', 'marker-table'],
fields: extensionApiMarkerSchemaFields,
},
{
name: 'ExtensionSuspend',
tableLabel:
"{marker.data.name}{marker.data.extensionId ? ' by ' : ''}{marker.data.extensionId}",
chartLabel:
"{marker.name} — {marker.data.name}{marker.data.extensionId ? ' by ' : ''}{marker.data.extensionId}",
display: ['marker-chart', 'marker-table'],
fields: [
{
key: 'name',
label: 'Details',
format: 'string',
containsPII: ['url'],
},
{
key: 'extensionId',
label: 'Extension ID',
format: 'string',
containsPII: ['extension-id'],
},
],
},
];

/**
* The marker schema comes from Gecko, and is embedded in the profile. However,
* we may want to define schemas that are front-end only. This is the location
Expand Down
104 changes: 99 additions & 5 deletions src/profile-logic/process-profile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ import {
import {
addPIICategoriesToMarkerSchema,
computeStringIndexMarkerFieldsByDataType,
extensionMarkerSchemas,
} from '../profile-logic/marker-schema';
import { convertJsTracerToThread } from '../profile-logic/js-tracer';

Expand Down Expand Up @@ -102,8 +103,10 @@ import type {
IndexIntoGeckoThreadStringTable,
GCSliceMarkerPayload,
GCMajorMarkerPayload,
ExtensionMarkerPayload,
MarkerPayload,
MarkerPayload_Gecko,
TextMarkerPayload,
GCSliceData_Gecko,
GCMajorCompleted,
GCMajorCompleted_Gecko,
Expand Down Expand Up @@ -788,16 +791,16 @@ function _processMarkers(
}
}

const markerName = stringArray[geckoMarkers.name[markerIndex]];
const payload = _processMarkerPayload(
markerName,
geckoPayload,
stringArray,
stringTable,
stringIndexMarkerFieldsByDataType,
stackIndexOffset
);
const name = stringTable.indexForString(
stringArray[geckoMarkers.name[markerIndex]]
);
const name = stringTable.indexForString(markerName);
const startTime = geckoMarkers.startTime[markerIndex];
const endTime = geckoMarkers.endTime[markerIndex];
const phase = geckoMarkers.phase[markerIndex];
Expand Down Expand Up @@ -851,11 +854,59 @@ function convertPhaseTimes(
return phases;
}

function _getExtensionMarkerPayloadFields(
markerName: string,
text: string
): Pick<ExtensionMarkerPayload, 'type' | 'name' | 'extensionId'> | null {
switch (markerName) {
case 'ExtensionParent':
case 'ExtensionChild': {
const match = /^(.*), (api_(?:call|event): [\s\S]*)$/.exec(text);
return match
? {
type: markerName,
name: match[2],
extensionId: match[1],
}
: null;
}
case 'Extension Suspend': {
const match = / by .*$/.exec(text);
return match === null
? null
: {
type: 'ExtensionSuspend',
name: text.slice(0, match.index),
extensionId: text.slice(match.index + ' by '.length),
};
}
default:
return null;
}
}

function _processExtensionTextMarkerPayload(
markerName: string,
payload: TextMarkerPayload,
stringArray: string[]
): ExtensionMarkerPayload | null {
const text =
typeof payload.name === 'number' ? stringArray[payload.name] : payload.name;
const fields = _getExtensionMarkerPayloadFields(markerName, text);
if (!fields) {
return null;
}

const { type: _type, name: _name, ...otherFields } = payload;
return { ...otherFields, ...fields };
}

/**
* Process just the marker payload. This converts stacks into causes, and augments
* the GC information.
* Process just the marker payload. This converts stacks into causes, augments
* the GC information, and converts extension text markers into structured payloads.
*/
function _processMarkerPayload(
markerName: string,
geckoPayload: MarkerPayload_Gecko | null,
stringArray: string[],
stringTable: StringTable,
Expand All @@ -872,6 +923,17 @@ function _processMarkerPayload(
// Warning: This function converts the payload into an any type.
const payload = _convertStackToCause(geckoPayload, stackIndexOffset);

if (payload.type === 'Text') {
const extensionPayload = _processExtensionTextMarkerPayload(
markerName,
payload,
stringArray
);
if (extensionPayload) {
return extensionPayload;
}
}

switch (payload.type) {
/*
* We want to improve the format of these markers to make them
Expand Down Expand Up @@ -1760,6 +1822,38 @@ function processMarkerSchema(geckoProfile: GeckoProfile): MarkerSchema[] {
}
}

const usedExtensionSchemaNames = new Set<string>();
for (const profile of [geckoProfile, ...geckoProfile.processes]) {
for (const thread of profile.threads) {
const { markers, stringTable } = thread;
for (const marker of markers.data) {
const payload = marker[markers.schema.data];
if (!payload || payload.type !== 'Text') {
continue;
}
const markerName = stringTable[marker[markers.schema.name]];
const text =
typeof payload.name === 'number'
? stringTable[payload.name]
: payload.name;
const fields = _getExtensionMarkerPayloadFields(markerName, text);
if (fields) {
usedExtensionSchemaNames.add(fields.type);
}
}
}
}

for (const markerSchema of extensionMarkerSchemas) {
if (
usedExtensionSchemaNames.has(markerSchema.name) &&
!names.has(markerSchema.name)
) {
names.add(markerSchema.name);
combinedSchemas.push(markerSchema);
}
}

return combinedSchemas;
}

Expand Down
Loading
Loading