Skip to content

Commit

Permalink
Deploy Oct 24, 2024 (#5177)
Browse files Browse the repository at this point in the history
[Markus Stange] Fix links to setup-startup-profiling.py post monorepo
migration. (#5162)
[Greg Tatum] Fix marker-only compare profiles (#5130)
[Greg Tatum] Copy over the profile meta when merging profiles (#5131)
[Nazım Can Altınova] Change the 'is-ready' postMessage pairs to
'ready:request' and 'ready:response' to be more explicit (#5148)
[Julien Wajsberg] Fix timestamp issues in local tests (#5167)

Also thanks to our localizers for contributing:
fur: Fabio Tomat
  • Loading branch information
canova authored Oct 24, 2024
2 parents cf8501c + 28a9332 commit afa401f
Show file tree
Hide file tree
Showing 11 changed files with 885 additions and 789 deletions.
6 changes: 3 additions & 3 deletions docs-developer/loading-in-profiles.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ Firefox loads the profiles directly into the front-end through a WebChannel mech
A profile can be injected via another website and the postMessage API.

First wait for the page to be ready. This can be done by posting an `{ name: 'is-ready' }` message and waiting for the response of a similar `{ name: 'is-ready' }`.
First wait for the page to be ready. This can be done by posting an `{ name: 'ready:request' }` message and waiting for the response of a similar `{ name: 'ready:response' }`.

```js
/**
Expand All @@ -133,7 +133,7 @@ function openProfile(profile) {
* @param {MessageEvent} event
*/
const listener = ({ data }) => {
if (data?.name === 'is-ready') {
if (data?.name === 'ready:response') {
console.log('The profiler is ready. Injecting the profile.');
isReady = true;
const message = {
Expand All @@ -148,7 +148,7 @@ function openProfile(profile) {
window.addEventListener('message', listener);
while (!isReady) {
await new Promise((resolve) => setTimeout(resolve, 100));
profilerWindow.postMessage({ name: 'is-ready' }, origin);
profilerWindow.postMessage({ name: 'ready:request' }, origin);
}

window.removeEventListener('message', listener);
Expand Down
8 changes: 4 additions & 4 deletions docs-user/guide-startup-shutdown.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,15 +58,15 @@ adb shell am start -n org.mozilla.geckoview_example/.App \

Fenix has a [different way](https://firefox-source-docs.mozilla.org/mobile/android/geckoview/consumer/automation.html#reading-configuration-from-a-file) to specify environment variables: it uses a yaml file.

The easiest way to set up startup profiling is to run the `<fenix-repo>/tools/setup-startup-profiling.py` script. For example:
The easiest way to set up startup profiling is to run the `<mozilla-central-repo>/mobile/android/fenix/tools/setup-startup-profiling.py` script. For example:
```bash
./tools/setup-startup-profiling.py activate nightly # To activate startup profiling on nightly.
./tools/setup-startup-profiling.py deactivate beta # To deactivate startup profiling on beta.
./mobile/android/fenix/tools/setup-startup-profiling.py activate nightly # To activate startup profiling on nightly.
./mobile/android/fenix/tools/setup-startup-profiling.py deactivate beta # To deactivate startup profiling on beta.
```

If the app is uninstalled or the device is restarted, the `activate` command may need to be re-run. The script is hard-coded to use a default configuration file with default profiling arguments. If you wish to change these arguments or use a non-standard app ID, modify the script locally or read below.

If you don't want to check out [the fenix repository](https://github.com/mozilla-mobile/fenix/), you should be able to download [the script standalone](https://raw.githubusercontent.com/mozilla-mobile/fenix/master/tools/setup-startup-profiling.py) and execute it.
If you don't want to check out [mozilla-central](https://hg.mozilla.org/mozilla-central/), you should be able to download [the script standalone](https://hg.mozilla.org/mozilla-central/raw-file/tip/mobile/android/fenix/tools/setup-startup-profiling.py) and execute it.

#### Manual configuration

Expand Down
11 changes: 11 additions & 0 deletions locales/fur/app.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -719,6 +719,13 @@ TabBar--marker-table-tab = Tabele marcadôrs
TabBar--network-tab = Rêt
TabBar--js-tracer-tab = Tracer JS
## TabSelectorMenu
## This component is a context menu that's opened when you click on the root
## range at the top left corner for profiler analysis view. It's used to switch
## between tabs that were captured in the profile.

TabSelectorMenu--all-tabs-and-windows = Dutis lis schedis e i barcons
## TrackContextMenu
## This is used as a context menu for timeline to organize the tracks in the
## analysis UI.
Expand All @@ -735,6 +742,10 @@ TrackContextMenu--hide-other-screenshots-tracks = Plate altris liniis Videadis.
TrackContextMenu--hide-track = Plate “{ $trackName }
TrackContextMenu--show-all-tracks = Mostre dutis lis liniis
TrackContextMenu--show-local-tracks-in-process = Mostre dutis lis liniis in chest procès
# This is used as the context menu item to hide all tracks of the selected track's type.
# Variables:
# $type (String) - Name of the type of selected track to hide.
TrackContextMenu--hide-all-tracks-by-selected-track-type = Plate dutis lis liniis di gjenar “{ $type }
# This is used in the tracks context menu as a button to show all the tracks
# that match the search filter.
TrackContextMenu--show-all-matching-tracks = Mostre dutis lis liniis corispondentis
Expand Down
22 changes: 11 additions & 11 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@
"mixedtuplemap": "^1.0.0",
"namedtuplemap": "^1.0.0",
"photon-colors": "^3.3.2",
"query-string": "^9.1.0",
"query-string": "^9.1.1",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-intersection-observer": "^9.13.1",
Expand All @@ -100,14 +100,14 @@
"workbox-window": "^7.1.0"
},
"devDependencies": {
"@babel/cli": "^7.25.6",
"@babel/core": "^7.25.2",
"@babel/eslint-parser": "^7.25.1",
"@babel/eslint-plugin": "^7.25.1",
"@babel/cli": "^7.25.7",
"@babel/core": "^7.25.7",
"@babel/eslint-parser": "^7.25.7",
"@babel/eslint-plugin": "^7.25.7",
"@babel/plugin-proposal-class-properties": "^7.18.6",
"@babel/preset-env": "^7.25.4",
"@babel/preset-flow": "^7.24.7",
"@babel/preset-react": "^7.24.7",
"@babel/preset-env": "^7.25.7",
"@babel/preset-flow": "^7.25.7",
"@babel/preset-react": "^7.25.7",
"@testing-library/dom": "^10.4.0",
"@testing-library/jest-dom": "^6.5.0",
"@testing-library/react": "^16.0.1",
Expand All @@ -117,7 +117,7 @@
"babel-loader": "^9.2.1",
"babel-plugin-module-resolver": "^5.0.2",
"browserslist": "^4.24.0",
"caniuse-lite": "^1.0.30001660",
"caniuse-lite": "^1.0.30001667",
"circular-dependency-plugin": "^5.2.1",
"codecov": "^3.8.3",
"copy-webpack-plugin": "^12.0.2",
Expand All @@ -129,7 +129,7 @@
"eslint-config-prettier": "^9.1.0",
"eslint-import-resolver-alias": "^1.1.2",
"eslint-plugin-flowtype": "^8.0.3",
"eslint-plugin-import": "^2.30.0",
"eslint-plugin-import": "^2.31.0",
"eslint-plugin-jest": "^28.8.3",
"eslint-plugin-jest-dom": "^5.4.0",
"eslint-plugin-jest-formatting": "^3.1.0",
Expand Down Expand Up @@ -161,7 +161,7 @@
"raw-loader": "^4.0.2",
"rimraf": "^5.0.10",
"style-loader": "^4.0.0",
"stylelint": "^16.9.0",
"stylelint": "^16.10.0",
"stylelint-config-idiomatic-order": "^10.0.0",
"stylelint-config-standard": "^36.0.1",
"webpack": "^5.95.0",
Expand Down
6 changes: 3 additions & 3 deletions src/actions/receive-profile.js
Original file line number Diff line number Diff line change
Expand Up @@ -1712,17 +1712,17 @@ export function retrieveProfileForRawUrl(
case 'inject-profile':
dispatch(viewProfileFromPostMessage(data.profile));
break;
case 'is-ready': {
case 'ready:request': {
// The "inject-profile" event could be coming from a variety of locations.
// It could come from a `window.open` call on another page. It could come
// from an addon. It could come from being embedded in an iframe. In order
// to generically support these cases allow the opener to poll for the
// "is-ready" message.
// "ready:response" message.
console.log(
'Responding via postMessage that the profiler is ready.'
);
const otherWindow = event.source ?? window;
otherWindow.postMessage({ name: 'is-ready' }, '*');
otherWindow.postMessage({ name: 'ready:response' }, '*');
break;
}
default:
Expand Down
26 changes: 25 additions & 1 deletion src/profile-logic/merge-compare.js
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,18 @@ export function mergeProfilesForDiffing(
}

const resultProfile = getEmptyProfile();

// Copy over identical values for the ProfileMeta.
for (const [key, value] of Object.entries(profiles[0].meta)) {
if (profiles.every((profile) => profile.meta[key] === value)) {
resultProfile.meta[key] = value;
}
}
// Ensure it has a copy of the marker schema and categories, even though these could
// be different between the two profiles.
resultProfile.meta.markerSchema = profiles[0].meta.markerSchema;
resultProfile.meta.categories = profiles[0].meta.categories;

resultProfile.meta.interval = Math.min(
...profiles.map((profile) => profile.meta.interval)
);
Expand Down Expand Up @@ -217,7 +229,19 @@ export function mergeProfilesForDiffing(

// We adjust the various times so that the 2 profiles are aligned at the
// start and the data is consistent.
const startTimeAdjustment = -thread.samples.time[0];
let startTimeAdjustment = 0;
if (thread.samples.length) {
startTimeAdjustment = -thread.samples.time[0];
} else if (thread.markers.length) {
for (const startTime of thread.markers.startTime) {
// Find the first marker startTime.
if (startTime !== null) {
startTimeAdjustment = -startTime;
break;
}
}
}

thread.samples = adjustTableTimestamps(thread.samples, startTimeAdjustment);
thread.markers = adjustMarkerTimestamps(
thread.markers,
Expand Down
8 changes: 6 additions & 2 deletions src/test/components/UrlManager.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -363,13 +363,17 @@ describe('UrlManager', function () {

await new Promise((resolve) => {
function listener({ data }) {
if (data && typeof data === 'object' && data.name === 'is-ready') {
if (
data &&
typeof data === 'object' &&
data.name === 'ready:response'
) {
resolve();
window.removeEventListener('message', listener);
}
}
window.addEventListener('message', listener);
window.postMessage({ name: 'is-ready' }, '*');
window.postMessage({ name: 'ready:request' }, '*');
});

window.postMessage(
Expand Down
4 changes: 4 additions & 0 deletions src/test/setup.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ import { autoMockResizeObserver } from './fixtures/mocks/resize-observer';

autoMockResizeObserver();

if (process.env.TZ !== 'UTC') {
throw new Error('Jest must be run from `yarn test`');
}

// Register TextDecoder and TextEncoder with the global scope.
// These are now available globally in nodejs, but not when running with jsdom
// in jest apparently.
Expand Down
34 changes: 33 additions & 1 deletion src/test/unit/merge-compare.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import {
} from '../fixtures/profiles/processed-profile';
import { markerSchemaForTests } from '../fixtures/profiles/marker-schema';
import { ensureExists } from 'firefox-profiler/utils/flow';

import { getTimeRangeIncludingAllThreads } from 'firefox-profiler/profile-logic/profile-data';
import type { Thread } from 'firefox-profiler/types';

describe('mergeProfilesForDiffing function', function () {
Expand Down Expand Up @@ -233,6 +233,38 @@ describe('mergeProfilesForDiffing function', function () {
expect(mergedProfileThreadB.nativeSymbols.libIndex).toEqual([1, 1]);
expect(mergedThread.nativeSymbols.libIndex).toEqual([0, 0, 1, 1]);
});

it('should use marker timing if there are no samples', () => {
const profiles = [
getProfileWithMarkers([
['Thread1 Marker1', 2],
['Thread1 Marker2', 3, 5],
['Thread1 Marker3', 6, 7],
]),

getProfileWithMarkers([
['Thread1 Marker1', 5],
['Thread1 Marker2', 6, 8],
['Thread1 Marker3', 10, 15],
]),
];

const profileState = stateFromLocation({
pathname: '/public/fakehash1/',
search: '?thread=0&v=3',
hash: '',
});

const { profile } = mergeProfilesForDiffing(profiles, [
profileState,
profileState,
]);

expect(getTimeRangeIncludingAllThreads(profile)).toEqual({
start: 0,
end: 11,
});
});
});

describe('mergeThreads function', function () {
Expand Down
2 changes: 1 addition & 1 deletion src/utils/window-console.js
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,7 @@ export function addDataToWindowObject(
// This will be imperfect because of float rounding errors but still better
// than not having them.
const ns = Math.trunc((ts - Math.trunc(ts)) * 10 ** 6);
return `${d.getFullYear()}-${pad(d.getUTCMonth() + 1, 2)}-${pad(d.getUTCDate(), 2)} ${pad(d.getUTCHours(), 2)}:${pad(d.getUTCMinutes(), 2)}:${pad(d.getUTCSeconds(), 2)}.${pad(d.getUTCMilliseconds(), 3)}${pad(ns, 6)} UTC`;
return `${d.getUTCFullYear()}-${pad(d.getUTCMonth() + 1, 2)}-${pad(d.getUTCDate(), 2)} ${pad(d.getUTCHours(), 2)}:${pad(d.getUTCMinutes(), 2)}:${pad(d.getUTCSeconds(), 2)}.${pad(d.getUTCMilliseconds(), 3)}${pad(ns, 6)} UTC`;
}

const logs = [];
Expand Down
Loading

0 comments on commit afa401f

Please sign in to comment.