From a616dfc739831c49ac0c34e351fdf46354428b6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gunnlaugur=20=C3=9E=C3=B3r=20Briem?= Date: Thu, 4 Jan 2024 18:05:29 +0000 Subject: [PATCH 01/55] feat: support from-url profiles in compare Support profiles with dataSource `from-url` in the comparison view. This change is simply applying the suggestion in https://github.com/firefox-devtools/profiler/issues/3589#issuecomment-934257702 and removing the preceding check for dataSource being `public`, and running `yarn lint-fix`. It seems to work just fine for me when I run this project locally. I tested by pasting in two `https://profiler.firefox.com/from-url/https%253A%252F%252F` URLs that I had open in other browser tabs, and I get an apparently functioning comparison view. Closes #3589 --- src/actions/receive-profile.js | 54 +++++++++++++++++++--------------- 1 file changed, 30 insertions(+), 24 deletions(-) diff --git a/src/actions/receive-profile.js b/src/actions/receive-profile.js index 3ad846ad77..47695865c6 100644 --- a/src/actions/receive-profile.js +++ b/src/actions/receive-profile.js @@ -1531,33 +1531,39 @@ export function retrieveProfilesToCompare( }) ); - const hasSupportedDatasources = profileStates.every( - (state) => state.dataSource === 'public' - ); - if (!hasSupportedDatasources) { - throw new Error( - 'Only public uploaded profiles are supported by the comparison function.' - ); - } - // Then we retrieve the profiles from the online store, and unserialize // and process them if needed. - const promises = profileStates.map(async ({ hash }) => { - const profileUrl = getProfileUrlForHash(hash); - const response: ProfileOrZip = await _fetchProfile({ - url: profileUrl, - onTemporaryError: (e: TemporaryError) => { - dispatch(temporaryError(e)); - }, - }); - if (response.responseType !== 'PROFILE') { - throw new Error('Expected to receive a profile from _fetchProfile'); - } - const serializedProfile = response.profile; + const promises = profileStates.map( + async ({ dataSource, hash, profileUrl }) => { + switch (dataSource) { + case 'public': + // Use a URL from the public store. + profileUrl = getProfileUrlForHash(hash); + break; + case 'from-url': + // Use the profile URL in the decoded state, decoded from the input URL. + break; + default: + throw new Error( + 'Only public uploaded profiles are supported by the comparison function.' + ); + } + const response: ProfileOrZip = await _fetchProfile({ + url: profileUrl, + onTemporaryError: (e: TemporaryError) => { + dispatch(temporaryError(e)); + }, + }); + if (response.responseType !== 'PROFILE') { + throw new Error('Expected to receive a profile from _fetchProfile'); + } + const serializedProfile = response.profile; - const profile = unserializeProfileOfArbitraryFormat(serializedProfile); - return profile; - }); + const profile = + unserializeProfileOfArbitraryFormat(serializedProfile); + return profile; + } + ); // Once all profiles have been fetched and unserialized, we can start // pushing them to a brand new profile. This resulting profile will keep From 9c30ab7d5e13063c28997f4fb660bb1fe759c5bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gunnlaugur=20=C3=9E=C3=B3r=20Briem?= Date: Mon, 8 Jan 2024 14:11:09 +0000 Subject: [PATCH 02/55] test: add case covering from-url URLs in compare --- src/test/store/receive-profile.test.js | 38 ++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/src/test/store/receive-profile.test.js b/src/test/store/receive-profile.test.js index df6b390eac..c5cc993d23 100644 --- a/src/test/store/receive-profile.test.js +++ b/src/test/store/receive-profile.test.js @@ -1853,6 +1853,44 @@ describe('actions/receive-profile', function () { expect(rootRange).toEqual({ start: 0, end: 9 }); }); + it('retrieves from-url profiles and puts them in the same view', async function () { + // /from-url/https%3A%2F%2Ffakeurl.com%2Ffakeprofile.json + const { profile1, profile2, resultProfile, globalTracks, rootRange } = + await setup(getSomeProfiles(), { + url1: 'https://fakeurl.com/from-url/https%3A%2F%2Ffakeurl.com%2Ffakeprofile1.json?thread=0', + url2: 'https://fakeurl.com/from-url/https%3A%2F%2Ffakeurl.com%2Ffakeprofile2.json?thread=0', + }); + + const expectedThreads = [ + { + ...profile1.threads[0], + pid: '0 from profile 1', + tid: '0 from profile 1', + isMainThread: true, + processName: 'Profile 1: Empty', + unregisterTime: getTimeRangeForThread(profile1.threads[0], 1).end, + }, + { + ...profile2.threads[0], + pid: '0 from profile 2', + tid: '0 from profile 2', + isMainThread: true, + processName: 'Profile 2: Empty', + unregisterTime: getTimeRangeForThread(profile2.threads[0], 1).end, + }, + // comparison thread + expect.objectContaining({ + processType: 'comparison', + pid: 'Diff between 1 and 2', + name: 'Diff between 1 and 2', + }), + ]; + + expect(resultProfile.threads).toEqual(expectedThreads); + expect(globalTracks).toHaveLength(3); // each thread + comparison track + expect(rootRange).toEqual({ start: 0, end: 9 }); + }); + it('expands the URL if needed', async function () { const { shortUrl1, shortUrl2, globalTracks, rootRange } = await setupWithShortUrl(getSomeProfiles(), { From 966a7331533ec6a456861a6bb1939cd82f80dc11 Mon Sep 17 00:00:00 2001 From: "Francesco Lodolo [:flod]" Date: Wed, 17 Jan 2024 14:04:00 +0000 Subject: [PATCH 03/55] Pontoon: Update Italian (it) localization of Firefox Profiler Co-authored-by: Francesco Lodolo [:flod] --- locales/it/app.ftl | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/locales/it/app.ftl b/locales/it/app.ftl index de7110071c..fff288c0cb 100644 --- a/locales/it/app.ftl +++ b/locales/it/app.ftl @@ -753,6 +753,46 @@ TrackPower--tooltip-energy-carbon-used-in-preview-milliwatthour = { $value } mWh TrackPower--tooltip-energy-carbon-used-in-preview-microwatthour = { $value } µWh ({ $carbonValue } mg CO₂e) .label = Energia utilizzata nella selezione corrente +## TrackBandwidth +## This is used to show how much data was transfered over time. +## For the strings in this group, the carbon dioxide equivalent is estimated +## from the amount of data transfered. +## The carbon dioxide equivalent represents the equivalent amount +## of CO₂ to achieve the same level of global warming potential. + +# This is used in the tooltip of the bandwidth track. +# Variables: +# $value (String) - the value for the data transfer speed. +# Will contain the unit (eg. B, KB, MB) +TrackBandwidthGraph--speed = { $value } al secondo + .label = Velocità di trasferimento per questo campione +# This is used in the tooltip of the bandwidth track. +# Variables: +# $value (String) - how many read or write operations were performed since the previous sample +TrackBandwidthGraph--read-write-operations-since-the-previous-sample = { $value } + .label = Operazioni di lettura/scrittura dal campione precedente +# This is used in the tooltip of the bandwidth track. +# Variables: +# $value (String) - the total of transfered data until the hovered time. +# Will contain the unit (eg. B, KB, MB) +# $carbonValue (string) - the carbon dioxide equivalent (CO₂e) value in grams +TrackBandwidthGraph--cumulative-bandwidth-at-this-time = { $value } ({ $carbonValue } g CO₂e) + .label = Dati trasferiti fino a questo momento +# This is used in the tooltip of the bandwidth track. +# Variables: +# $value (String) - the total of transfered data during the visible time range. +# Will contain the unit (eg. B, KB, MB) +# $carbonValue (string) - the carbon dioxide equivalent (CO₂e) value in grams +TrackBandwidthGraph--total-bandwidth-in-graph = { $value } ({ $carbonValue } g CO₂e) + .label = Dati trasferiti nell’intervallo visibile +# This is used in the tooltip of the bandwidth track when a range is selected. +# Variables: +# $value (String) - the total of transfered data during the selected time range. +# Will contain the unit (eg. B, KB, MB) +# $carbonValue (string) - the carbon dioxide equivalent (CO₂e) value in grams +TrackBandwidthGraph--total-bandwidth-in-range = { $value } ({ $carbonValue } g CO₂e) + .label = Dati trasferiti nella selezione corrente + ## TrackSearchField ## The component that is used for the search input in the track context menu. From 822f8e2131677ac351236947a26709e05ccbc9bb Mon Sep 17 00:00:00 2001 From: Pontoon Date: Wed, 17 Jan 2024 14:22:55 +0000 Subject: [PATCH 04/55] Pontoon: Update Chinese (China) (zh-CN) localization of Firefox Profiler --- locales/zh-CN/app.ftl | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/locales/zh-CN/app.ftl b/locales/zh-CN/app.ftl index 358e5cc87b..0dcd3c2917 100644 --- a/locales/zh-CN/app.ftl +++ b/locales/zh-CN/app.ftl @@ -748,6 +748,14 @@ TrackPower--tooltip-energy-carbon-used-in-preview-milliwatthour = { $value } mWh TrackPower--tooltip-energy-carbon-used-in-preview-microwatthour = { $value } µWh ({ $carbonValue } mg CO2e) .label = 当前选择范围内的能耗 +## TrackBandwidth +## This is used to show how much data was transfered over time. +## For the strings in this group, the carbon dioxide equivalent is estimated +## from the amount of data transfered. +## The carbon dioxide equivalent represents the equivalent amount +## of CO₂ to achieve the same level of global warming potential. + + ## TrackSearchField ## The component that is used for the search input in the track context menu. From 78813c38f687678770e20294ea0f225aba9d9892 Mon Sep 17 00:00:00 2001 From: Pin-guang Chen Date: Wed, 17 Jan 2024 14:22:57 +0000 Subject: [PATCH 05/55] Pontoon: Update Chinese (Taiwan) (zh-TW) localization of Firefox Profiler Co-authored-by: Pin-guang Chen --- locales/zh-TW/app.ftl | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/locales/zh-TW/app.ftl b/locales/zh-TW/app.ftl index 822381a1a0..64af7ef56d 100644 --- a/locales/zh-TW/app.ftl +++ b/locales/zh-TW/app.ftl @@ -747,6 +747,46 @@ TrackPower--tooltip-energy-carbon-used-in-preview-milliwatthour = { $value } mWh TrackPower--tooltip-energy-carbon-used-in-preview-microwatthour = { $value } µWh({ $carbonValue } mg CO₂e) .label = 目前選擇範圍中消耗的能源 +## TrackBandwidth +## This is used to show how much data was transfered over time. +## For the strings in this group, the carbon dioxide equivalent is estimated +## from the amount of data transfered. +## The carbon dioxide equivalent represents the equivalent amount +## of CO₂ to achieve the same level of global warming potential. + +# This is used in the tooltip of the bandwidth track. +# Variables: +# $value (String) - the value for the data transfer speed. +# Will contain the unit (eg. B, KB, MB) +TrackBandwidthGraph--speed = 每秒 { $value } + .label = 此樣本的傳輸速度 +# This is used in the tooltip of the bandwidth track. +# Variables: +# $value (String) - how many read or write operations were performed since the previous sample +TrackBandwidthGraph--read-write-operations-since-the-previous-sample = { $value } + .label = 自從上次取樣後的讀寫操作數量 +# This is used in the tooltip of the bandwidth track. +# Variables: +# $value (String) - the total of transfered data until the hovered time. +# Will contain the unit (eg. B, KB, MB) +# $carbonValue (string) - the carbon dioxide equivalent (CO₂e) value in grams +TrackBandwidthGraph--cumulative-bandwidth-at-this-time = { $value }({ $carbonValue } g CO₂e) + .label = 至此刻為止傳輸的資料量 +# This is used in the tooltip of the bandwidth track. +# Variables: +# $value (String) - the total of transfered data during the visible time range. +# Will contain the unit (eg. B, KB, MB) +# $carbonValue (string) - the carbon dioxide equivalent (CO₂e) value in grams +TrackBandwidthGraph--total-bandwidth-in-graph = { $value }({ $carbonValue } g CO₂e) + .label = 可見範圍中傳輸的資料量 +# This is used in the tooltip of the bandwidth track when a range is selected. +# Variables: +# $value (String) - the total of transfered data during the selected time range. +# Will contain the unit (eg. B, KB, MB) +# $carbonValue (string) - the carbon dioxide equivalent (CO₂e) value in grams +TrackBandwidthGraph--total-bandwidth-in-range = { $value }({ $carbonValue } g CO₂e) + .label = 目前選擇範圍傳輸的資料量 + ## TrackSearchField ## The component that is used for the search input in the track context menu. From e40be9873f2b6b25a60a45dfca079070d188e880 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20K=C3=B6hler?= Date: Wed, 17 Jan 2024 15:13:40 +0000 Subject: [PATCH 06/55] Pontoon: Update German (de) localization of Firefox Profiler MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Michael Köhler --- locales/de/app.ftl | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/locales/de/app.ftl b/locales/de/app.ftl index e962678b6e..05aecffc7a 100644 --- a/locales/de/app.ftl +++ b/locales/de/app.ftl @@ -811,6 +811,46 @@ TrackPower--tooltip-energy-carbon-used-in-preview-milliwatthour = { $value } mWh TrackPower--tooltip-energy-carbon-used-in-preview-microwatthour = { $value } µWh ({ $carbonValue } mg CO₂e) .label = In der aktuellen Auswahl verwendete Energie +## TrackBandwidth +## This is used to show how much data was transfered over time. +## For the strings in this group, the carbon dioxide equivalent is estimated +## from the amount of data transfered. +## The carbon dioxide equivalent represents the equivalent amount +## of CO₂ to achieve the same level of global warming potential. + +# This is used in the tooltip of the bandwidth track. +# Variables: +# $value (String) - the value for the data transfer speed. +# Will contain the unit (eg. B, KB, MB) +TrackBandwidthGraph--speed = { $value } pro Sekunde + .label = Übertragungsgeschwindigkeit für diese Teilmenge +# This is used in the tooltip of the bandwidth track. +# Variables: +# $value (String) - how many read or write operations were performed since the previous sample +TrackBandwidthGraph--read-write-operations-since-the-previous-sample = { $value } + .label = Lese-/Schreiboperationen seit der vorherigen Teilmenge +# This is used in the tooltip of the bandwidth track. +# Variables: +# $value (String) - the total of transfered data until the hovered time. +# Will contain the unit (eg. B, KB, MB) +# $carbonValue (string) - the carbon dioxide equivalent (CO₂e) value in grams +TrackBandwidthGraph--cumulative-bandwidth-at-this-time = { $value } ({ $carbonValue } g CO₂e) + .label = Bislang übertragene Daten +# This is used in the tooltip of the bandwidth track. +# Variables: +# $value (String) - the total of transfered data during the visible time range. +# Will contain the unit (eg. B, KB, MB) +# $carbonValue (string) - the carbon dioxide equivalent (CO₂e) value in grams +TrackBandwidthGraph--total-bandwidth-in-graph = { $value } ({ $carbonValue } g CO₂e) + .label = Im sichtbaren Bereich übertragene Daten +# This is used in the tooltip of the bandwidth track when a range is selected. +# Variables: +# $value (String) - the total of transfered data during the selected time range. +# Will contain the unit (eg. B, KB, MB) +# $carbonValue (string) - the carbon dioxide equivalent (CO₂e) value in grams +TrackBandwidthGraph--total-bandwidth-in-range = { $value } ({ $carbonValue } g CO₂e) + .label = In der aktuellen Auswahl übertragene Daten + ## TrackSearchField ## The component that is used for the search input in the track context menu. From 7b93e3bc45beeac79551255dc1885e11bd800374 Mon Sep 17 00:00:00 2001 From: Valery Ledovskoy Date: Wed, 17 Jan 2024 20:33:38 +0000 Subject: [PATCH 07/55] Pontoon: Update Russian (ru) localization of Firefox Profiler Co-authored-by: Valery Ledovskoy --- locales/ru/app.ftl | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/locales/ru/app.ftl b/locales/ru/app.ftl index ff40225294..b1728a4dbf 100644 --- a/locales/ru/app.ftl +++ b/locales/ru/app.ftl @@ -849,6 +849,46 @@ TrackPower--tooltip-energy-carbon-used-in-preview-milliwatthour = { $value } м TrackPower--tooltip-energy-carbon-used-in-preview-microwatthour = { $value } мкВт-ч ({ $carbonValue } мг CO₂e) .label = Энергия, использованная в текущей выборке +## TrackBandwidth +## This is used to show how much data was transfered over time. +## For the strings in this group, the carbon dioxide equivalent is estimated +## from the amount of data transfered. +## The carbon dioxide equivalent represents the equivalent amount +## of CO₂ to achieve the same level of global warming potential. + +# This is used in the tooltip of the bandwidth track. +# Variables: +# $value (String) - the value for the data transfer speed. +# Will contain the unit (eg. B, KB, MB) +TrackBandwidthGraph--speed = { $value } в секунду + .label = Скорость передачи для этого семпла +# This is used in the tooltip of the bandwidth track. +# Variables: +# $value (String) - how many read or write operations were performed since the previous sample +TrackBandwidthGraph--read-write-operations-since-the-previous-sample = { $value } + .label = операции чтения/записи с момента передачи предыдущего семпла +# This is used in the tooltip of the bandwidth track. +# Variables: +# $value (String) - the total of transfered data until the hovered time. +# Will contain the unit (eg. B, KB, MB) +# $carbonValue (string) - the carbon dioxide equivalent (CO₂e) value in grams +TrackBandwidthGraph--cumulative-bandwidth-at-this-time = { $value } ({ $carbonValue } г CO₂e) + .label = Данные, переданные до этого времени +# This is used in the tooltip of the bandwidth track. +# Variables: +# $value (String) - the total of transfered data during the visible time range. +# Will contain the unit (eg. B, KB, MB) +# $carbonValue (string) - the carbon dioxide equivalent (CO₂e) value in grams +TrackBandwidthGraph--total-bandwidth-in-graph = { $value } ({ $carbonValue } г CO₂e) + .label = Данные, передаваемые в видимом диапазоне +# This is used in the tooltip of the bandwidth track when a range is selected. +# Variables: +# $value (String) - the total of transfered data during the selected time range. +# Will contain the unit (eg. B, KB, MB) +# $carbonValue (string) - the carbon dioxide equivalent (CO₂e) value in grams +TrackBandwidthGraph--total-bandwidth-in-range = { $value } ({ $carbonValue } г CO₂e) + .label = Данные, переданные в текущей выборке + ## TrackSearchField ## The component that is used for the search input in the track context menu. From e11673b4e9e0d8c5fdd620159e50f777c525382f Mon Sep 17 00:00:00 2001 From: Andreas Pettersson Date: Wed, 17 Jan 2024 23:53:28 +0000 Subject: [PATCH 08/55] Pontoon: Update Swedish (sv-SE) localization of Firefox Profiler Co-authored-by: Andreas Pettersson Co-authored-by: Luna Jernberg --- locales/sv-SE/app.ftl | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/locales/sv-SE/app.ftl b/locales/sv-SE/app.ftl index ec20082711..baa13da525 100644 --- a/locales/sv-SE/app.ftl +++ b/locales/sv-SE/app.ftl @@ -830,6 +830,46 @@ TrackPower--tooltip-energy-carbon-used-in-preview-milliwatthour = { $value } mWh TrackPower--tooltip-energy-carbon-used-in-preview-microwatthour = { $value } µWh ({ $carbonValue } mg CO₂e) .label = Energi som används i det aktuella urvalet +## TrackBandwidth +## This is used to show how much data was transfered over time. +## For the strings in this group, the carbon dioxide equivalent is estimated +## from the amount of data transfered. +## The carbon dioxide equivalent represents the equivalent amount +## of CO₂ to achieve the same level of global warming potential. + +# This is used in the tooltip of the bandwidth track. +# Variables: +# $value (String) - the value for the data transfer speed. +# Will contain the unit (eg. B, KB, MB) +TrackBandwidthGraph--speed = { $value } per sekund + .label = Överföringshastighet för detta prov +# This is used in the tooltip of the bandwidth track. +# Variables: +# $value (String) - how many read or write operations were performed since the previous sample +TrackBandwidthGraph--read-write-operations-since-the-previous-sample = { $value } + .label = Läs/skrivoperationer sedan föregående prov +# This is used in the tooltip of the bandwidth track. +# Variables: +# $value (String) - the total of transfered data until the hovered time. +# Will contain the unit (eg. B, KB, MB) +# $carbonValue (string) - the carbon dioxide equivalent (CO₂e) value in grams +TrackBandwidthGraph--cumulative-bandwidth-at-this-time = { $value } ({ $carbonValue } g CO₂e) + .label = Data överförd till denna tid +# This is used in the tooltip of the bandwidth track. +# Variables: +# $value (String) - the total of transfered data during the visible time range. +# Will contain the unit (eg. B, KB, MB) +# $carbonValue (string) - the carbon dioxide equivalent (CO₂e) value in grams +TrackBandwidthGraph--total-bandwidth-in-graph = { $value } ({ $carbonValue } g CO₂e) + .label = Data som överförs i det synliga intervallet +# This is used in the tooltip of the bandwidth track when a range is selected. +# Variables: +# $value (String) - the total of transfered data during the selected time range. +# Will contain the unit (eg. B, KB, MB) +# $carbonValue (string) - the carbon dioxide equivalent (CO₂e) value in grams +TrackBandwidthGraph--total-bandwidth-in-range = { $value } ({ $carbonValue } g CO₂e) + .label = Data som överförs i det aktuella valet + ## TrackSearchField ## The component that is used for the search input in the track context menu. From 4df508b2acab04c13b00228fbe0c7c4f9e2e49e3 Mon Sep 17 00:00:00 2001 From: Mark Heijl Date: Thu, 18 Jan 2024 08:22:46 +0000 Subject: [PATCH 09/55] Pontoon: Update Dutch (nl) localization of Firefox Profiler Co-authored-by: Mark Heijl --- locales/nl/app.ftl | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/locales/nl/app.ftl b/locales/nl/app.ftl index 0136544cfc..371132a390 100644 --- a/locales/nl/app.ftl +++ b/locales/nl/app.ftl @@ -835,6 +835,41 @@ TrackPower--tooltip-energy-carbon-used-in-preview-milliwatthour = { $value } mWh TrackPower--tooltip-energy-carbon-used-in-preview-microwatthour = { $value } µWh ({ $carbonValue } mg CO₂e) .label = Energie gebruikt in de huidige selectie +## TrackBandwidth +## This is used to show how much data was transfered over time. +## For the strings in this group, the carbon dioxide equivalent is estimated +## from the amount of data transfered. +## The carbon dioxide equivalent represents the equivalent amount +## of CO₂ to achieve the same level of global warming potential. + +# This is used in the tooltip of the bandwidth track. +# Variables: +# $value (String) - the value for the data transfer speed. +# Will contain the unit (eg. B, KB, MB) +TrackBandwidthGraph--speed = { $value } per seconde + .label = Overzetsnelheid voor deze opname +# This is used in the tooltip of the bandwidth track. +# Variables: +# $value (String) - the total of transfered data until the hovered time. +# Will contain the unit (eg. B, KB, MB) +# $carbonValue (string) - the carbon dioxide equivalent (CO₂e) value in grams +TrackBandwidthGraph--cumulative-bandwidth-at-this-time = { $value } ({ $carbonValue } g CO₂e) + .label = Overgezette gegevens tot nu toe +# This is used in the tooltip of the bandwidth track. +# Variables: +# $value (String) - the total of transfered data during the visible time range. +# Will contain the unit (eg. B, KB, MB) +# $carbonValue (string) - the carbon dioxide equivalent (CO₂e) value in grams +TrackBandwidthGraph--total-bandwidth-in-graph = { $value } ({ $carbonValue } g CO₂e) + .label = Overgezette gegevens in het zichtbare bereik +# This is used in the tooltip of the bandwidth track when a range is selected. +# Variables: +# $value (String) - the total of transfered data during the selected time range. +# Will contain the unit (eg. B, KB, MB) +# $carbonValue (string) - the carbon dioxide equivalent (CO₂e) value in grams +TrackBandwidthGraph--total-bandwidth-in-range = { $value } ({ $carbonValue } g CO₂e) + .label = Overgezette gegevens in de huidige selectie + ## TrackSearchField ## The component that is used for the search input in the track context menu. From 22ee900862714faf8897c59a2f7687f152b3bdb9 Mon Sep 17 00:00:00 2001 From: Fjoerfoks Date: Thu, 18 Jan 2024 08:33:06 +0000 Subject: [PATCH 10/55] Pontoon: Update Frisian (fy-NL) localization of Firefox Profiler Co-authored-by: Fjoerfoks --- locales/fy-NL/app.ftl | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/locales/fy-NL/app.ftl b/locales/fy-NL/app.ftl index 3f634432ed..b1d25a0220 100644 --- a/locales/fy-NL/app.ftl +++ b/locales/fy-NL/app.ftl @@ -835,6 +835,41 @@ TrackPower--tooltip-energy-carbon-used-in-preview-milliwatthour = { $value } mWh TrackPower--tooltip-energy-carbon-used-in-preview-microwatthour = { $value } µWh ({ $carbonValue } mg CO₂e) .label = Enerzjy brûkt yn de aktuele seleksje +## TrackBandwidth +## This is used to show how much data was transfered over time. +## For the strings in this group, the carbon dioxide equivalent is estimated +## from the amount of data transfered. +## The carbon dioxide equivalent represents the equivalent amount +## of CO₂ to achieve the same level of global warming potential. + +# This is used in the tooltip of the bandwidth track. +# Variables: +# $value (String) - the value for the data transfer speed. +# Will contain the unit (eg. B, KB, MB) +TrackBandwidthGraph--speed = { $value } per sekonde + .label = Oersetsnelheid foar dizze opname +# This is used in the tooltip of the bandwidth track. +# Variables: +# $value (String) - the total of transfered data until the hovered time. +# Will contain the unit (eg. B, KB, MB) +# $carbonValue (string) - the carbon dioxide equivalent (CO₂e) value in grams +TrackBandwidthGraph--cumulative-bandwidth-at-this-time = { $value } ({ $carbonValue } g CO₂e) + .label = Oersette gegevens oant no ta +# This is used in the tooltip of the bandwidth track. +# Variables: +# $value (String) - the total of transfered data during the visible time range. +# Will contain the unit (eg. B, KB, MB) +# $carbonValue (string) - the carbon dioxide equivalent (CO₂e) value in grams +TrackBandwidthGraph--total-bandwidth-in-graph = { $value } ({ $carbonValue } g CO₂e) + .label = Oersette gegevens yn it sichtbere berik +# This is used in the tooltip of the bandwidth track when a range is selected. +# Variables: +# $value (String) - the total of transfered data during the selected time range. +# Will contain the unit (eg. B, KB, MB) +# $carbonValue (string) - the carbon dioxide equivalent (CO₂e) value in grams +TrackBandwidthGraph--total-bandwidth-in-range = { $value } ({ $carbonValue } g CO₂e) + .label = Oersette gegevens yn de aktuele seleksje + ## TrackSearchField ## The component that is used for the search input in the track context menu. From e1448f6c50d3dc1057a9ad7fc51d329ff3b3c192 Mon Sep 17 00:00:00 2001 From: Marcelo Ghelman Date: Thu, 18 Jan 2024 12:12:34 +0000 Subject: [PATCH 11/55] Pontoon: Update Portuguese (Brazil) (pt-BR) localization of Firefox Profiler Co-authored-by: Marcelo Ghelman --- locales/pt-BR/app.ftl | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/locales/pt-BR/app.ftl b/locales/pt-BR/app.ftl index 41475c03ff..b2684f283c 100644 --- a/locales/pt-BR/app.ftl +++ b/locales/pt-BR/app.ftl @@ -764,6 +764,46 @@ TrackPower--tooltip-energy-carbon-used-in-preview-milliwatthour = { $value } mWh TrackPower--tooltip-energy-carbon-used-in-preview-microwatthour = { $value } µWh ({ $carbonValue } mg CO₂e) .label = Energia usada na seleção atual +## TrackBandwidth +## This is used to show how much data was transfered over time. +## For the strings in this group, the carbon dioxide equivalent is estimated +## from the amount of data transfered. +## The carbon dioxide equivalent represents the equivalent amount +## of CO₂ to achieve the same level of global warming potential. + +# This is used in the tooltip of the bandwidth track. +# Variables: +# $value (String) - the value for the data transfer speed. +# Will contain the unit (eg. B, KB, MB) +TrackBandwidthGraph--speed = { $value } por segundo + .label = Velocidade de transferência desta amostra +# This is used in the tooltip of the bandwidth track. +# Variables: +# $value (String) - how many read or write operations were performed since the previous sample +TrackBandwidthGraph--read-write-operations-since-the-previous-sample = { $value } + .label = operações de leitura/escrita desde a amostra anterior +# This is used in the tooltip of the bandwidth track. +# Variables: +# $value (String) - the total of transfered data until the hovered time. +# Will contain the unit (eg. B, KB, MB) +# $carbonValue (string) - the carbon dioxide equivalent (CO₂e) value in grams +TrackBandwidthGraph--cumulative-bandwidth-at-this-time = { $value } ({ $carbonValue } g CO₂e) + .label = Dados transferidos até agora +# This is used in the tooltip of the bandwidth track. +# Variables: +# $value (String) - the total of transfered data during the visible time range. +# Will contain the unit (eg. B, KB, MB) +# $carbonValue (string) - the carbon dioxide equivalent (CO₂e) value in grams +TrackBandwidthGraph--total-bandwidth-in-graph = { $value } ({ $carbonValue } g CO₂e) + .label = Dados transferidos no intervalo visível +# This is used in the tooltip of the bandwidth track when a range is selected. +# Variables: +# $value (String) - the total of transfered data during the selected time range. +# Will contain the unit (eg. B, KB, MB) +# $carbonValue (string) - the carbon dioxide equivalent (CO₂e) value in grams +TrackBandwidthGraph--total-bandwidth-in-range = { $value } ({ $carbonValue } g CO₂e) + .label = Dados transferidos na seleção atual + ## TrackSearchField ## The component that is used for the search input in the track context menu. From 86710aba5719228b4cc8f823e3a90b1f544f0682 Mon Sep 17 00:00:00 2001 From: Fjoerfoks Date: Thu, 18 Jan 2024 16:20:44 +0000 Subject: [PATCH 12/55] Pontoon: Update Frisian (fy-NL) localization of Firefox Profiler Co-authored-by: Fjoerfoks --- locales/fy-NL/app.ftl | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/locales/fy-NL/app.ftl b/locales/fy-NL/app.ftl index b1d25a0220..85acb2cd75 100644 --- a/locales/fy-NL/app.ftl +++ b/locales/fy-NL/app.ftl @@ -850,6 +850,11 @@ TrackBandwidthGraph--speed = { $value } per sekonde .label = Oersetsnelheid foar dizze opname # This is used in the tooltip of the bandwidth track. # Variables: +# $value (String) - how many read or write operations were performed since the previous sample +TrackBandwidthGraph--read-write-operations-since-the-previous-sample = { $value } + .label = lês/skriuw-útfieringen sûnt de lêste opname +# This is used in the tooltip of the bandwidth track. +# Variables: # $value (String) - the total of transfered data until the hovered time. # Will contain the unit (eg. B, KB, MB) # $carbonValue (string) - the carbon dioxide equivalent (CO₂e) value in grams From 36e25c5c2622fcfde7bb138bc1a53d61e4bfad75 Mon Sep 17 00:00:00 2001 From: Ian Neal Date: Thu, 18 Jan 2024 18:06:05 +0000 Subject: [PATCH 13/55] Pontoon: Update English (Great Britain) (en-GB) localization of Firefox Profiler Co-authored-by: Ian Neal --- locales/en-GB/app.ftl | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/locales/en-GB/app.ftl b/locales/en-GB/app.ftl index 8bfb221080..d7f30d91a8 100644 --- a/locales/en-GB/app.ftl +++ b/locales/en-GB/app.ftl @@ -835,6 +835,46 @@ TrackPower--tooltip-energy-carbon-used-in-preview-milliwatthour = { $value } mWh TrackPower--tooltip-energy-carbon-used-in-preview-microwatthour = { $value } µWh ({ $carbonValue } mg CO₂e) .label = Energy used in the current selection +## TrackBandwidth +## This is used to show how much data was transfered over time. +## For the strings in this group, the carbon dioxide equivalent is estimated +## from the amount of data transfered. +## The carbon dioxide equivalent represents the equivalent amount +## of CO₂ to achieve the same level of global warming potential. + +# This is used in the tooltip of the bandwidth track. +# Variables: +# $value (String) - the value for the data transfer speed. +# Will contain the unit (eg. B, KB, MB) +TrackBandwidthGraph--speed = { $value } per second + .label = Transfer speed for this sample +# This is used in the tooltip of the bandwidth track. +# Variables: +# $value (String) - how many read or write operations were performed since the previous sample +TrackBandwidthGraph--read-write-operations-since-the-previous-sample = { $value } + .label = read/write operations since the previous sample +# This is used in the tooltip of the bandwidth track. +# Variables: +# $value (String) - the total of transfered data until the hovered time. +# Will contain the unit (eg. B, KB, MB) +# $carbonValue (string) - the carbon dioxide equivalent (CO₂e) value in grams +TrackBandwidthGraph--cumulative-bandwidth-at-this-time = { $value } ({ $carbonValue } g CO₂e) + .label = Data transferred up to this time +# This is used in the tooltip of the bandwidth track. +# Variables: +# $value (String) - the total of transfered data during the visible time range. +# Will contain the unit (eg. B, KB, MB) +# $carbonValue (string) - the carbon dioxide equivalent (CO₂e) value in grams +TrackBandwidthGraph--total-bandwidth-in-graph = { $value } ({ $carbonValue } g CO₂e) + .label = Data transferred in the visible range +# This is used in the tooltip of the bandwidth track when a range is selected. +# Variables: +# $value (String) - the total of transfered data during the selected time range. +# Will contain the unit (eg. B, KB, MB) +# $carbonValue (string) - the carbon dioxide equivalent (CO₂e) value in grams +TrackBandwidthGraph--total-bandwidth-in-range = { $value } ({ $carbonValue } g CO₂e) + .label = Data transferred in the current selection + ## TrackSearchField ## The component that is used for the search input in the track context menu. From f521d83edbc2c8e8732eda0a7fa95b4258780409 Mon Sep 17 00:00:00 2001 From: Olvcpr423 Date: Thu, 18 Jan 2024 18:42:44 +0000 Subject: [PATCH 14/55] Pontoon: Update Chinese (China) (zh-CN) localization of Firefox Profiler Co-authored-by: Olvcpr423 --- locales/zh-CN/app.ftl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/locales/zh-CN/app.ftl b/locales/zh-CN/app.ftl index 0dcd3c2917..7e3c770fcf 100644 --- a/locales/zh-CN/app.ftl +++ b/locales/zh-CN/app.ftl @@ -777,7 +777,7 @@ TrackSearchField--search-input = # See: https://profiler.firefox.com/docs/#/./guide-filtering-call-trees?id=collapse # Variables: # $item (String) - Name of the current thread. E.g.: Web Content. -TransformNavigator--complete = 完成“{ $item }” +TransformNavigator--complete = 完整“{ $item }” # "Collapse resource" transform. # See: https://profiler.firefox.com/docs/#/./guide-filtering-call-trees?id=collapse # Variables: From 0059e954907345b9187e3ee6f89f842c7be0beda Mon Sep 17 00:00:00 2001 From: Jim Spentzos Date: Thu, 18 Jan 2024 23:03:58 +0000 Subject: [PATCH 15/55] Pontoon: Update Greek (el) localization of Firefox Profiler Co-authored-by: Jim Spentzos --- locales/el/app.ftl | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/locales/el/app.ftl b/locales/el/app.ftl index 979308fea6..a3eba1eeb7 100644 --- a/locales/el/app.ftl +++ b/locales/el/app.ftl @@ -830,6 +830,46 @@ TrackPower--tooltip-energy-carbon-used-in-preview-milliwatthour = { $value } mWh TrackPower--tooltip-energy-carbon-used-in-preview-microwatthour = { $value } µWh ({ $carbonValue } mg CO₂e) .label = Η ενέργεια που χρησιμοποιείται στην τρέχουσα επιλογή +## TrackBandwidth +## This is used to show how much data was transfered over time. +## For the strings in this group, the carbon dioxide equivalent is estimated +## from the amount of data transfered. +## The carbon dioxide equivalent represents the equivalent amount +## of CO₂ to achieve the same level of global warming potential. + +# This is used in the tooltip of the bandwidth track. +# Variables: +# $value (String) - the value for the data transfer speed. +# Will contain the unit (eg. B, KB, MB) +TrackBandwidthGraph--speed = { $value } ανά δευτερόλεπτο + .label = Ταχύτητα μεταφοράς για αυτό το δείγμα +# This is used in the tooltip of the bandwidth track. +# Variables: +# $value (String) - how many read or write operations were performed since the previous sample +TrackBandwidthGraph--read-write-operations-since-the-previous-sample = { $value } + .label = λειτουργίες ανάγνωσης/εγγραφής από το προηγούμενο δείγμα +# This is used in the tooltip of the bandwidth track. +# Variables: +# $value (String) - the total of transfered data until the hovered time. +# Will contain the unit (eg. B, KB, MB) +# $carbonValue (string) - the carbon dioxide equivalent (CO₂e) value in grams +TrackBandwidthGraph--cumulative-bandwidth-at-this-time = { $value } ({ $carbonValue } g CO₂e) + .label = Δεδομένα που μεταφέρθηκαν μέχρι αυτήν τη στιγμή +# This is used in the tooltip of the bandwidth track. +# Variables: +# $value (String) - the total of transfered data during the visible time range. +# Will contain the unit (eg. B, KB, MB) +# $carbonValue (string) - the carbon dioxide equivalent (CO₂e) value in grams +TrackBandwidthGraph--total-bandwidth-in-graph = { $value } ({ $carbonValue } g CO₂e) + .label = Δεδομένα που μεταφέρθηκαν στο ορατό εύρος +# This is used in the tooltip of the bandwidth track when a range is selected. +# Variables: +# $value (String) - the total of transfered data during the selected time range. +# Will contain the unit (eg. B, KB, MB) +# $carbonValue (string) - the carbon dioxide equivalent (CO₂e) value in grams +TrackBandwidthGraph--total-bandwidth-in-range = { $value } ({ $carbonValue } g CO₂e) + .label = Δεδομένα που μεταφέρθηκαν στην τρέχουσα επιλογή + ## TrackSearchField ## The component that is used for the search input in the track context menu. From 86633a3f00d7cefc1d94f20e2f7d3a576f81a19a Mon Sep 17 00:00:00 2001 From: "depfu[bot]" <23717796+depfu[bot]@users.noreply.github.com> Date: Fri, 19 Jan 2024 11:17:55 +0100 Subject: [PATCH 16/55] =?UTF-8?q?=E2=AC=86=EF=B8=8F=20Update=20copy-webpac?= =?UTF-8?q?k-plugin=20to=20version=2012.0.2=20(#4891)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: depfu[bot] <23717796+depfu[bot]@users.noreply.github.com> --- package.json | 2 +- yarn.lock | 92 ++++++++++++++++++++++++++++++---------------------- 2 files changed, 55 insertions(+), 39 deletions(-) diff --git a/package.json b/package.json index 4d752335ad..90bb28235b 100644 --- a/package.json +++ b/package.json @@ -117,7 +117,7 @@ "caniuse-lite": "^1.0.30001572", "circular-dependency-plugin": "^5.2.1", "codecov": "^3.8.3", - "copy-webpack-plugin": "^11.0.0", + "copy-webpack-plugin": "^12.0.2", "cross-env": "^7.0.3", "css-loader": "^6.9.0", "cssnano": "^6.0.3", diff --git a/yarn.lock b/yarn.lock index 24670b143a..c2bc3e974f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1852,6 +1852,11 @@ resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-5.3.0.tgz#0ec9264cf54a527671d990eb874e030b55b70dcc" integrity sha512-CX6t4SYQ37lzxicAqsBtxA3OseeoVrh9cSJ5PFYam0GksYlupRfy1A+Q4aYD3zvcfECLc0zO2u+ZnR2UYKvCrw== +"@sindresorhus/merge-streams@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@sindresorhus/merge-streams/-/merge-streams-1.0.0.tgz#9cd84cc15bc865a5ca35fcaae198eb899f7b5c90" + integrity sha512-rUV5WyJrJLoloD4NDN1V1+LDMDWOa4OTsT4yYJwQNpTU6FWxkxHpL7eu4w+DmiH8x/EAM1otkPE1+LaspIbplw== + "@sinonjs/commons@^2.0.0": version "2.0.0" resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-2.0.0.tgz#fd4ca5b063554307e8327b4564bd56d3b73924a3" @@ -2661,7 +2666,7 @@ ajv-keywords@^3.5.2: resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d" integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ== -ajv-keywords@^5.0.0: +ajv-keywords@^5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-5.1.0.tgz#69d4d385a4733cdbeab44964a1170a88f87f0e16" integrity sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw== @@ -2678,10 +2683,10 @@ ajv@^6.10.0, ajv@^6.10.2, ajv@^6.12.4, ajv@^6.12.5: json-schema-traverse "^0.4.1" uri-js "^4.2.2" -ajv@^8.0.0, ajv@^8.0.1, ajv@^8.6.0, ajv@^8.8.0: - version "8.11.0" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.11.0.tgz#977e91dd96ca669f54a11e23e378e33b884a565f" - integrity sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg== +ajv@^8.0.0, ajv@^8.0.1, ajv@^8.6.0, ajv@^8.9.0: + version "8.12.0" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.12.0.tgz#d1a0527323e22f53562c567c00991577dfbe19d1" + integrity sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA== dependencies: fast-deep-equal "^3.1.1" json-schema-traverse "^1.0.0" @@ -3920,17 +3925,17 @@ copy-to@^2.0.1: resolved "https://registry.yarnpkg.com/copy-to/-/copy-to-2.0.1.tgz#2680fbb8068a48d08656b6098092bdafc906f4a5" integrity sha1-JoD7uAaKSNCGVrYJgJK9r8kG9KU= -copy-webpack-plugin@^11.0.0: - version "11.0.0" - resolved "https://registry.yarnpkg.com/copy-webpack-plugin/-/copy-webpack-plugin-11.0.0.tgz#96d4dbdb5f73d02dd72d0528d1958721ab72e04a" - integrity sha512-fX2MWpamkW0hZxMEg0+mYnA40LTosOSa5TqZ9GYIBzyJa9C3QUaMPSE2xAi/buNr8u89SfD9wHSQVBzrRa/SOQ== +copy-webpack-plugin@^12.0.2: + version "12.0.2" + resolved "https://registry.yarnpkg.com/copy-webpack-plugin/-/copy-webpack-plugin-12.0.2.tgz#935e57b8e6183c82f95bd937df658a59f6a2da28" + integrity sha512-SNwdBeHyII+rWvee/bTnAYyO8vfVdcSTud4EIb6jcZ8inLeWucJE0DnxXQBjlQ5zlteuuvooGQy3LIyGxhvlOA== dependencies: - fast-glob "^3.2.11" + fast-glob "^3.3.2" glob-parent "^6.0.1" - globby "^13.1.1" + globby "^14.0.0" normalize-path "^3.0.0" - schema-utils "^4.0.0" - serialize-javascript "^6.0.0" + schema-utils "^4.2.0" + serialize-javascript "^6.0.2" core-js-compat@^3.31.0, core-js-compat@^3.33.1: version "3.33.2" @@ -5367,7 +5372,7 @@ fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== -fast-glob@^3.2.11, fast-glob@^3.2.9, fast-glob@^3.3.1, fast-glob@^3.3.2: +fast-glob@^3.2.9, fast-glob@^3.3.1, fast-glob@^3.3.2: version "3.3.2" resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.2.tgz#a904501e57cfdd2ffcded45e99a54fef55e46129" integrity sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow== @@ -6039,16 +6044,17 @@ globby@^11.1.0: merge2 "^1.4.1" slash "^3.0.0" -globby@^13.1.1: - version "13.1.2" - resolved "https://registry.yarnpkg.com/globby/-/globby-13.1.2.tgz#29047105582427ab6eca4f905200667b056da515" - integrity sha512-LKSDZXToac40u8Q1PQtZihbNdTYSNMuWe+K5l+oa6KgDzSvVrHXlJy40hUP522RjAIoNLJYBJi7ow+rbFpIhHQ== +globby@^14.0.0: + version "14.0.0" + resolved "https://registry.yarnpkg.com/globby/-/globby-14.0.0.tgz#ea9c062a3614e33f516804e778590fcf055256b9" + integrity sha512-/1WM/LNHRAOH9lZta77uGbq0dAEQM+XjNesWwhlERDVenqothRbnzTrL3/LrIoEPPjeUHC3vrS6TwoyxeHs7MQ== dependencies: - dir-glob "^3.0.1" - fast-glob "^3.2.11" - ignore "^5.2.0" - merge2 "^1.4.1" - slash "^4.0.0" + "@sindresorhus/merge-streams" "^1.0.0" + fast-glob "^3.3.2" + ignore "^5.2.4" + path-type "^5.0.0" + slash "^5.1.0" + unicorn-magic "^0.1.0" globjoin@^0.1.4: version "0.1.4" @@ -6583,7 +6589,7 @@ ignore@^4.0.6: resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg== -ignore@^5.0.0, ignore@^5.2.0, ignore@^5.3.0: +ignore@^5.0.0, ignore@^5.2.0, ignore@^5.2.4, ignore@^5.3.0: version "5.3.0" resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.0.tgz#67418ae40d34d6999c95ff56016759c718c82f78" integrity sha512-g7dmpshy+gD7mh88OC9NwSGTKoc3kyLAZQRU1mt53Aw/vnvfXnbC+F/7F7QoYVKbV+KNvJx8wArewKy1vXMtlg== @@ -9944,6 +9950,11 @@ path-type@^4.0.0: resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== +path-type@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-5.0.0.tgz#14b01ed7aea7ddf9c7c3f46181d4d04f9c785bb8" + integrity sha512-5HviZNaZcfqP95rwpv+1HDgUamezbqdSYTyzjTvwtJSnIH+3vnbmWsItli8OFEndS984VT55M3jduxZbX351gg== + pause-stream@0.0.11: version "0.0.11" resolved "https://registry.yarnpkg.com/pause-stream/-/pause-stream-0.0.11.tgz#fe5a34b0cbce12b5aa6a2b403ee2e73b602f1445" @@ -11302,15 +11313,15 @@ schema-utils@^3.0.0, schema-utils@^3.1.1, schema-utils@^3.2.0: ajv "^6.12.5" ajv-keywords "^3.5.2" -schema-utils@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-4.0.0.tgz#60331e9e3ae78ec5d16353c467c34b3a0a1d3df7" - integrity sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg== +schema-utils@^4.0.0, schema-utils@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-4.2.0.tgz#70d7c93e153a273a805801882ebd3bff20d89c8b" + integrity sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw== dependencies: "@types/json-schema" "^7.0.9" - ajv "^8.8.0" + ajv "^8.9.0" ajv-formats "^2.1.1" - ajv-keywords "^5.0.0" + ajv-keywords "^5.1.0" select-hose@^2.0.0: version "2.0.0" @@ -11384,10 +11395,10 @@ serialize-javascript@^4.0.0: dependencies: randombytes "^2.1.0" -serialize-javascript@^6.0.0, serialize-javascript@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.1.tgz#b206efb27c3da0b0ab6b52f48d170b7996458e5c" - integrity sha512-owoXEFjWRllis8/M1Q+Cw5k8ZH40e3zhp/ovX+Xr/vi1qj6QesbyXXViFbpNvWvPNAD62SutwEXavefrLJWj7w== +serialize-javascript@^6.0.1, serialize-javascript@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.2.tgz#defa1e055c83bf6d59ea805d8da862254eb6a6c2" + integrity sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g== dependencies: randombytes "^2.1.0" @@ -11531,10 +11542,10 @@ slash@^3.0.0: resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== -slash@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/slash/-/slash-4.0.0.tgz#2422372176c4c6c5addb5e2ada885af984b396a7" - integrity sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew== +slash@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-5.1.0.tgz#be3adddcdf09ac38eebe8dcdc7b1a57a75b095ce" + integrity sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg== slice-ansi@^2.1.0: version "2.1.0" @@ -12637,6 +12648,11 @@ unicode-property-aliases-ecmascript@^2.0.0: resolved "https://registry.yarnpkg.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.0.0.tgz#0a36cb9a585c4f6abd51ad1deddb285c165297c8" integrity sha512-5Zfuy9q/DFr4tfO7ZPeVXb1aPoeQSdeFMLpYuFebehDAhbuevLs5yxSZmIFN1tP5F9Wl4IpJrYojg85/zgyZHQ== +unicorn-magic@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/unicorn-magic/-/unicorn-magic-0.1.0.tgz#1bb9a51c823aaf9d73a8bfcd3d1a23dde94b0ce4" + integrity sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ== + unified-diff@^4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/unified-diff/-/unified-diff-4.0.1.tgz#511a8dfd94da3543ac67eb42d538465b2ac64a0e" From 2443b5b8c554867c4762ba103fe93a20f1f57785 Mon Sep 17 00:00:00 2001 From: ravmn Date: Fri, 19 Jan 2024 11:22:24 +0000 Subject: [PATCH 17/55] Pontoon: Update Spanish (Chile) (es-CL) localization of Firefox Profiler Co-authored-by: ravmn --- locales/es-CL/app.ftl | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/locales/es-CL/app.ftl b/locales/es-CL/app.ftl index 337b8aae66..77039372c7 100644 --- a/locales/es-CL/app.ftl +++ b/locales/es-CL/app.ftl @@ -765,6 +765,46 @@ TrackPower--tooltip-energy-carbon-used-in-preview-milliwatthour = { $value } mWh TrackPower--tooltip-energy-carbon-used-in-preview-microwatthour = { $value } µWh ({ $carbonValue } mg CO₂e) .label = Energía usada en la selección actual +## TrackBandwidth +## This is used to show how much data was transfered over time. +## For the strings in this group, the carbon dioxide equivalent is estimated +## from the amount of data transfered. +## The carbon dioxide equivalent represents the equivalent amount +## of CO₂ to achieve the same level of global warming potential. + +# This is used in the tooltip of the bandwidth track. +# Variables: +# $value (String) - the value for the data transfer speed. +# Will contain the unit (eg. B, KB, MB) +TrackBandwidthGraph--speed = { $value } por segundo + .label = Velocidad de transferencia para esta muestra +# This is used in the tooltip of the bandwidth track. +# Variables: +# $value (String) - how many read or write operations were performed since the previous sample +TrackBandwidthGraph--read-write-operations-since-the-previous-sample = { $value } + .label = operaciones de lectura/escritura desde la muestra anterior +# This is used in the tooltip of the bandwidth track. +# Variables: +# $value (String) - the total of transfered data until the hovered time. +# Will contain the unit (eg. B, KB, MB) +# $carbonValue (string) - the carbon dioxide equivalent (CO₂e) value in grams +TrackBandwidthGraph--cumulative-bandwidth-at-this-time = { $value } ({ $carbonValue } g CO₂e) + .label = Datos transferidos a la fecha +# This is used in the tooltip of the bandwidth track. +# Variables: +# $value (String) - the total of transfered data during the visible time range. +# Will contain the unit (eg. B, KB, MB) +# $carbonValue (string) - the carbon dioxide equivalent (CO₂e) value in grams +TrackBandwidthGraph--total-bandwidth-in-graph = { $value } ({ $carbonValue } g CO₂e) + .label = Datos transferidos en el rango visible +# This is used in the tooltip of the bandwidth track when a range is selected. +# Variables: +# $value (String) - the total of transfered data during the selected time range. +# Will contain the unit (eg. B, KB, MB) +# $carbonValue (string) - the carbon dioxide equivalent (CO₂e) value in grams +TrackBandwidthGraph--total-bandwidth-in-range = { $value } ({ $carbonValue } g CO₂e) + .label = Datos transferidos en la selección actual + ## TrackSearchField ## The component that is used for the search input in the track context menu. From aadeb38bd8775dd81939538802268edf554ff4eb Mon Sep 17 00:00:00 2001 From: Olvcpr423 Date: Fri, 19 Jan 2024 17:15:43 +0000 Subject: [PATCH 18/55] Pontoon: Update Chinese (China) (zh-CN) localization of Firefox Profiler Co-authored-by: Olvcpr423 --- locales/zh-CN/app.ftl | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/locales/zh-CN/app.ftl b/locales/zh-CN/app.ftl index 7e3c770fcf..0dcba03a4a 100644 --- a/locales/zh-CN/app.ftl +++ b/locales/zh-CN/app.ftl @@ -755,6 +755,31 @@ TrackPower--tooltip-energy-carbon-used-in-preview-microwatthour = { $value } µW ## The carbon dioxide equivalent represents the equivalent amount ## of CO₂ to achieve the same level of global warming potential. +# This is used in the tooltip of the bandwidth track. +# Variables: +# $value (String) - the value for the data transfer speed. +# Will contain the unit (eg. B, KB, MB) +TrackBandwidthGraph--speed = { $value } 每秒 + .label = 此样本的传输速度 +# This is used in the tooltip of the bandwidth track. +# Variables: +# $value (String) - how many read or write operations were performed since the previous sample +TrackBandwidthGraph--read-write-operations-since-the-previous-sample = { $value } + .label = 上次采样结束后发生的读写操作数 +# This is used in the tooltip of the bandwidth track. +# Variables: +# $value (String) - the total of transfered data until the hovered time. +# Will contain the unit (eg. B, KB, MB) +# $carbonValue (string) - the carbon dioxide equivalent (CO₂e) value in grams +TrackBandwidthGraph--cumulative-bandwidth-at-this-time = { $value }({ $carbonValue } g CO₂e) + .label = 目前为止传输的数据 +# This is used in the tooltip of the bandwidth track. +# Variables: +# $value (String) - the total of transfered data during the visible time range. +# Will contain the unit (eg. B, KB, MB) +# $carbonValue (string) - the carbon dioxide equivalent (CO₂e) value in grams +TrackBandwidthGraph--total-bandwidth-in-graph = { $value }({ $carbonValue } g CO₂e) + .label = 可见范围内传输的数据 ## TrackSearchField ## The component that is used for the search input in the track context menu. From 7236ea44154fd418b02016742fb755bc61814807 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 19 Jan 2024 18:16:11 +0100 Subject: [PATCH 19/55] Bump jinja2 from 3.1.2 to 3.1.3 in /taskcluster (#4881) Bumps [jinja2](https://github.com/pallets/jinja) from 3.1.2 to 3.1.3. - [Release notes](https://github.com/pallets/jinja/releases) - [Changelog](https://github.com/pallets/jinja/blob/main/CHANGES.rst) - [Commits](https://github.com/pallets/jinja/compare/3.1.2...3.1.3) --- updated-dependencies: - dependency-name: jinja2 dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Julien Wajsberg --- taskcluster/requirements.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/taskcluster/requirements.txt b/taskcluster/requirements.txt index 0d36608dcb..74d9e9e96c 100644 --- a/taskcluster/requirements.txt +++ b/taskcluster/requirements.txt @@ -117,9 +117,9 @@ idna==3.4 \ --hash=sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4 \ --hash=sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2 # via requests -jinja2==3.1.2 \ - --hash=sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852 \ - --hash=sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61 +jinja2==3.1.3 \ + --hash=sha256:7d6d50dd97d52cbc355597bd845fabfbac3f551e1f99619e39a35ce8c370b5fa \ + --hash=sha256:ac8bd6544d4bb2c9792bf3a159e80bba8fda7f07e81bc3aed565432d5925ba90 # via cookiecutter json-e==4.5.3 \ --hash=sha256:4d40ed481d19e475bb4b21807675a34b077a19197385007fed0ca4e867b5d9cd \ From 5bed53a97e1fb03acc86d8e4f0442e04dd61d5ea Mon Sep 17 00:00:00 2001 From: Fjoerfoks Date: Mon, 22 Jan 2024 06:42:47 +0000 Subject: [PATCH 20/55] Pontoon: Update Dutch (nl) localization of Firefox Profiler Co-authored-by: Fjoerfoks --- locales/nl/app.ftl | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/locales/nl/app.ftl b/locales/nl/app.ftl index 371132a390..446b7900c3 100644 --- a/locales/nl/app.ftl +++ b/locales/nl/app.ftl @@ -850,6 +850,11 @@ TrackBandwidthGraph--speed = { $value } per seconde .label = Overzetsnelheid voor deze opname # This is used in the tooltip of the bandwidth track. # Variables: +# $value (String) - how many read or write operations were performed since the previous sample +TrackBandwidthGraph--read-write-operations-since-the-previous-sample = { $value } + .label = lees/schrijf-uitvoeringen sinds de laatste opname +# This is used in the tooltip of the bandwidth track. +# Variables: # $value (String) - the total of transfered data until the hovered time. # Will contain the unit (eg. B, KB, MB) # $carbonValue (string) - the carbon dioxide equivalent (CO₂e) value in grams From 48e87ca5e48f7669a2a0f5cf14f165df25acaa88 Mon Sep 17 00:00:00 2001 From: Markus Stange Date: Sun, 21 Jan 2024 22:10:45 -0500 Subject: [PATCH 21/55] Make mergeFunction fast. Before: https://share.firefox.dev/3SbzpQy (1422 samples in mergeFunction) After: https://share.firefox.dev/3tVp3fQ (46 samples in mergeFunction, 30x faster) --- src/profile-logic/transforms.js | 75 ++++--- .../__snapshots__/profile-view.test.js.snap | 193 +++++++++++------- 2 files changed, 164 insertions(+), 104 deletions(-) diff --git a/src/profile-logic/transforms.js b/src/profile-logic/transforms.js index b5cc20ebfa..5f0ec3d565 100644 --- a/src/profile-logic/transforms.js +++ b/src/profile-logic/transforms.js @@ -794,7 +794,7 @@ export function mergeCallNode( } /** - * Go through the StackTable and remove any stacks that are part of the given function. + * Go through the StackTable and "skip" any stacks with the given function. * This operation effectively merges the timing of the stacks into their callers. */ export function mergeFunction( @@ -802,45 +802,56 @@ export function mergeFunction( funcIndexToMerge: IndexIntoFuncTable ): Thread { const { stackTable, frameTable } = thread; - const oldStackToNewStack: Map< - IndexIntoStackTable | null, - IndexIntoStackTable | null, - > = new Map(); - // A root stack's prefix will be null. Maintain that relationship from old to new - // stacks by mapping from null to null. - oldStackToNewStack.set(null, null); - const newStackTable = getEmptyStackTable(); + + // A map oldStack -> newStack+1, implemented as a Uint32Array for performance. + // If newStack+1 is zero it means "null", i.e. this stack was filtered out. + // Typed arrays are initialized to zero, which we interpret as null. + // + // For each old stack, the new stack is computed as follows: + // - If the old stack's function is not funcIndexToMerge, then the new stack + // is the same as the old stack. + // - If the old stack's function is funcIndexToMerge, then the new stack is + // the closest ancestor whose func is not funcIndexToMerge, or null if no + // such ancestor exists. + // + // We only compute a new prefix column; the other columns are copied from the + // old stack table. The skipped stacks are "orphaned"; they'll still be present + // in the new stack table but not referenced by samples or other stacks. + const oldStackToNewStackPlusOne = new Uint32Array(stackTable.length); + + const stackTableFrameCol = stackTable.frame; + const frameTableFuncCol = frameTable.func; + const oldPrefixCol = stackTable.prefix; + const newPrefixCol = new Array(stackTable.length); for (let stackIndex = 0; stackIndex < stackTable.length; stackIndex++) { - const prefix = stackTable.prefix[stackIndex]; - const frameIndex = stackTable.frame[stackIndex]; - const category = stackTable.category[stackIndex]; - const subcategory = stackTable.subcategory[stackIndex]; - const funcIndex = frameTable.func[frameIndex]; + const oldPrefix = oldPrefixCol[stackIndex]; + const newPrefixPlusOne = + oldPrefix === null ? 0 : oldStackToNewStackPlusOne[oldPrefix]; + const frameIndex = stackTableFrameCol[stackIndex]; + const funcIndex = frameTableFuncCol[frameIndex]; if (funcIndex === funcIndexToMerge) { - const newStackPrefix = oldStackToNewStack.get(prefix); - oldStackToNewStack.set( - stackIndex, - newStackPrefix === undefined ? null : newStackPrefix - ); + oldStackToNewStackPlusOne[stackIndex] = newPrefixPlusOne; } else { - const newStackIndex = newStackTable.length++; - const newStackPrefix = oldStackToNewStack.get(prefix); - newStackTable.prefix[newStackIndex] = - newStackPrefix === undefined ? null : newStackPrefix; - newStackTable.frame[newStackIndex] = frameIndex; - newStackTable.category[newStackIndex] = category; - newStackTable.subcategory[newStackIndex] = subcategory; - oldStackToNewStack.set(stackIndex, newStackIndex); + oldStackToNewStackPlusOne[stackIndex] = stackIndex + 1; } + const newPrefix = newPrefixPlusOne === 0 ? null : newPrefixPlusOne - 1; + newPrefixCol[stackIndex] = newPrefix; } - return updateThreadStacks( - thread, - newStackTable, - getMapStackUpdater(oldStackToNewStack) - ); + const newStackTable = { + ...stackTable, + prefix: newPrefixCol, + }; + + return updateThreadStacks(thread, newStackTable, (oldStack) => { + if (oldStack === null) { + return null; + } + const newStackPlusOne = oldStackToNewStackPlusOne[oldStack]; + return newStackPlusOne === 0 ? null : newStackPlusOne - 1; + }); } /** diff --git a/src/test/store/__snapshots__/profile-view.test.js.snap b/src/test/store/__snapshots__/profile-view.test.js.snap index 8b7999d51d..04a4821036 100644 --- a/src/test/store/__snapshots__/profile-view.test.js.snap +++ b/src/test/store/__snapshots__/profile-view.test.js.snap @@ -2193,11 +2193,13 @@ Object { 0, 0, 0, + 0, ], "depth": Array [ 0, 1, 2, + 2, 3, 2, 3, @@ -2207,6 +2209,7 @@ Object { "func": Int32Array [ 0, 1, + 2, 3, 4, 5, @@ -2220,18 +2223,20 @@ Object { 0, 0, 0, + 0, 2, 0, 0, ], - "length": 8, + "length": 9, "maxDepth": 3, "nextSibling": Int32Array [ -1, -1, - 4, + 3, + 5, -1, - 6, + 7, -1, -1, -1, @@ -2240,11 +2245,12 @@ Object { -1, 0, 1, - 2, 1, - 4, + 3, 1, - 6, + 5, + 1, + 7, ], "sourceFramesInlinedIntoSymbol": Array [ null, @@ -2255,6 +2261,7 @@ Object { null, null, null, + null, ], "subcategory": Int32Array [ 0, @@ -2265,16 +2272,18 @@ Object { 0, 0, 0, + 0, ], "subtreeRangeEnd": Uint32Array [ - 8, - 8, - 4, - 4, - 6, - 6, - 8, - 8, + 9, + 9, + 3, + 5, + 5, + 7, + 7, + 9, + 9, ], }, "isInverted": false, @@ -2287,6 +2296,7 @@ Object { 5, 6, 7, + 8, ], } `; @@ -2298,6 +2308,7 @@ CallTree { 1, 0, 0, + 0, 1, 0, 0, @@ -2314,11 +2325,13 @@ CallTree { 0, 0, 0, + 0, ], "depth": Array [ 0, 1, 2, + 2, 3, 2, 3, @@ -2328,6 +2341,7 @@ CallTree { "func": Int32Array [ 0, 1, + 2, 3, 4, 5, @@ -2341,18 +2355,20 @@ CallTree { 0, 0, 0, + 0, 2, 0, 0, ], - "length": 8, + "length": 9, "maxDepth": 3, "nextSibling": Int32Array [ -1, -1, - 4, + 3, + 5, -1, - 6, + 7, -1, -1, -1, @@ -2361,11 +2377,12 @@ CallTree { -1, 0, 1, - 2, 1, - 4, + 3, 1, - 6, + 5, + 1, + 7, ], "sourceFramesInlinedIntoSymbol": Array [ null, @@ -2376,6 +2393,7 @@ CallTree { null, null, null, + null, ], "subcategory": Int32Array [ 0, @@ -2386,16 +2404,18 @@ CallTree { 0, 0, 0, + 0, ], "subtreeRangeEnd": Uint32Array [ - 8, - 8, - 4, - 4, - 6, - 6, - 8, - 8, + 9, + 9, + 3, + 5, + 5, + 7, + 7, + 9, + 9, ], }, "isInverted": false, @@ -2408,6 +2428,7 @@ CallTree { 5, 6, 7, + 8, ], }, "_callNodeSummary": Object { @@ -2417,6 +2438,7 @@ CallTree { 0, 0, 0, + 0, 2, 0, 0, @@ -2427,6 +2449,7 @@ CallTree { 0, 0, 0, + 0, 2, 0, 0, @@ -2436,6 +2459,7 @@ CallTree { 2, 0, 0, + 0, 2, 2, 0, @@ -2452,11 +2476,13 @@ CallTree { 0, 0, 0, + 0, ], "depth": Array [ 0, 1, 2, + 2, 3, 2, 3, @@ -2466,6 +2492,7 @@ CallTree { "func": Int32Array [ 0, 1, + 2, 3, 4, 5, @@ -2479,18 +2506,20 @@ CallTree { 0, 0, 0, + 0, 2, 0, 0, ], - "length": 8, + "length": 9, "maxDepth": 3, "nextSibling": Int32Array [ -1, -1, - 4, + 3, + 5, -1, - 6, + 7, -1, -1, -1, @@ -2499,11 +2528,12 @@ CallTree { -1, 0, 1, - 2, 1, - 4, + 3, 1, - 6, + 5, + 1, + 7, ], "sourceFramesInlinedIntoSymbol": Array [ null, @@ -2514,6 +2544,7 @@ CallTree { null, null, null, + null, ], "subcategory": Int32Array [ 0, @@ -2524,16 +2555,18 @@ CallTree { 0, 0, 0, + 0, ], "subtreeRangeEnd": Uint32Array [ - 8, - 8, - 4, - 4, - 6, - 6, - 8, - 8, + 9, + 9, + 3, + 5, + 5, + 7, + 7, + 9, + 9, ], }, "_categories": Array [ @@ -2831,8 +2864,8 @@ CallTree { ], "length": 2, "stack": Array [ - 5, - 5, + 6, + 6, ], "time": Array [ 4, @@ -2851,10 +2884,12 @@ CallTree { 0, 0, 0, + 0, ], "frame": Array [ 0, 1, + 2, 3, 4, 5, @@ -2862,16 +2897,17 @@ CallTree { 7, 8, ], - "length": 8, + "length": 9, "prefix": Array [ null, 0, 1, - 2, 1, - 4, + 3, 1, - 6, + 5, + 1, + 7, ], "subcategory": Array [ 0, @@ -2882,6 +2918,7 @@ CallTree { 0, 0, 0, + 0, ], }, "stringTable": UniqueStringArray { @@ -3150,10 +3187,10 @@ Object { ], "length": 4, "stack": Array [ - 5, - 5, - 5, - 7, + 6, + 6, + 6, + 8, ], "time": Array [ 3, @@ -3174,10 +3211,12 @@ Object { 0, 0, 0, + 0, ], "frame": Array [ 0, 1, + 2, 3, 4, 5, @@ -3185,16 +3224,17 @@ Object { 7, 8, ], - "length": 8, + "length": 9, "prefix": Array [ null, 0, 1, - 2, 1, - 4, + 3, 1, - 6, + 5, + 1, + 7, ], "subcategory": Array [ 0, @@ -3205,6 +3245,7 @@ Object { 0, 0, 0, + 0, ], }, "stringTable": UniqueStringArray { @@ -3270,7 +3311,7 @@ Array [ }, Object { "callNode": Array [ - 4, + 5, ], "end": Array [ 1, @@ -3285,7 +3326,7 @@ Array [ }, Object { "callNode": Array [ - 5, + 6, ], "end": Array [ 1, @@ -3549,8 +3590,8 @@ Object { ], "length": 2, "stack": Array [ - 5, - 5, + 6, + 6, ], "time": Array [ 4, @@ -3569,10 +3610,12 @@ Object { 0, 0, 0, + 0, ], "frame": Array [ 0, 1, + 2, 3, 4, 5, @@ -3580,16 +3623,17 @@ Object { 7, 8, ], - "length": 8, + "length": 9, "prefix": Array [ null, 0, 1, - 2, 1, - 4, + 3, 1, - 6, + 5, + 1, + 7, ], "subcategory": Array [ 0, @@ -3600,6 +3644,7 @@ Object { 0, 0, 0, + 0, ], }, "stringTable": UniqueStringArray { @@ -3866,10 +3911,10 @@ Object { ], "length": 4, "stack": Array [ - 5, - 5, - 5, - 7, + 6, + 6, + 6, + 8, ], "time": Array [ 3, @@ -3890,10 +3935,12 @@ Object { 0, 0, 0, + 0, ], "frame": Array [ 0, 1, + 2, 3, 4, 5, @@ -3901,16 +3948,17 @@ Object { 7, 8, ], - "length": 8, + "length": 9, "prefix": Array [ null, 0, 1, - 2, 1, - 4, + 3, 1, - 6, + 5, + 1, + 7, ], "subcategory": Array [ 0, @@ -3921,6 +3969,7 @@ Object { 0, 0, 0, + 0, ], }, "stringTable": UniqueStringArray { From 8ee921addae3d24e316d3707bb203df49db32183 Mon Sep 17 00:00:00 2001 From: Markus Stange Date: Sat, 21 Oct 2023 22:20:07 -0400 Subject: [PATCH 22/55] Move call node path/index conversion functions into CallNodeInfo. This makes CallNodeInfo an interface, and puts the conversion functions between call node indexes and call node paths onto this new interface. It also provides accessor methods for the traditional members. This reduces the number of direct uses of the call node table. It will allow us to have two implementations in the future: One for the non-inverted call tree, and one for the inverted call tree. --- src/actions/profile-view.js | 28 +- src/components/calltree/CallTree.js | 10 +- src/components/flame-graph/Canvas.js | 12 +- src/components/flame-graph/FlameGraph.js | 35 +-- src/components/shared/CallNodeContextMenu.js | 29 +- src/components/shared/thread/StackGraph.js | 6 +- src/components/stack-chart/Canvas.js | 10 +- src/components/stack-chart/index.js | 16 +- src/components/tooltip/CallNode.js | 3 +- src/profile-logic/address-timings.js | 10 +- src/profile-logic/call-node-info.js | 180 +++++++++++++ src/profile-logic/call-tree.js | 14 +- src/profile-logic/line-timings.js | 10 +- src/profile-logic/profile-data.js | 249 +++++------------- src/profile-logic/stack-timing.js | 2 +- src/profile-logic/transforms.js | 15 +- src/reducers/profile-view.js | 7 +- src/selectors/per-thread/index.js | 4 +- src/selectors/per-thread/stack-sample.js | 37 ++- src/test/fixtures/utils.js | 2 +- .../__snapshots__/profile-view.test.js.snap | 18 +- src/test/store/actions.test.js | 6 +- src/test/store/bottom-box.test.js | 19 +- src/test/store/profile-view.test.js | 6 +- src/test/unit/address-timings.test.js | 3 +- src/test/unit/line-timings.test.js | 3 +- src/test/unit/profile-data.test.js | 59 ++--- src/test/unit/profile-tree.test.js | 47 ++-- src/types/actions.js | 4 +- src/types/profile-derived.js | 44 +++- 30 files changed, 464 insertions(+), 424 deletions(-) create mode 100644 src/profile-logic/call-node-info.js diff --git a/src/actions/profile-view.js b/src/actions/profile-view.js index 5f5a443bfb..76e9c32098 100644 --- a/src/actions/profile-view.js +++ b/src/actions/profile-view.js @@ -36,7 +36,6 @@ import { getInvertCallstack, getHash, } from 'firefox-profiler/selectors/url-state'; -import { getCallNodePathFromIndex } from 'firefox-profiler/profile-logic/profile-data'; import { assertExhaustiveCheck, getFirstItemFromSet, @@ -176,10 +175,7 @@ export function selectLeafCallNode( dispatch( changeSelectedCallNode( threadsKey, - getCallNodePathFromIndex( - newSelectedCallNode, - callNodeInfo.callNodeTable - ) + callNodeInfo.getCallNodePathFromIndex(newSelectedCallNode) ) ); }; @@ -207,13 +203,12 @@ export function selectRootCallNode( dispatch(changeSelectedCallNode(threadsKey, [])); return; } - const newSelectedCallNode = - callNodeInfo.stackIndexToCallNodeIndex[newSelectedStack]; + const stackIndexToCallNodeIndex = + callNodeInfo.getStackIndexToCallNodeIndex(); + const newSelectedCallNode = stackIndexToCallNodeIndex[newSelectedStack]; - const selectedCallNodePath = getCallNodePathFromIndex( - newSelectedCallNode, - callNodeInfo.callNodeTable - ); + const selectedCallNodePath = + callNodeInfo.getCallNodePathFromIndex(newSelectedCallNode); const rootCallNodePath = [selectedCallNodePath[0]]; dispatch( @@ -1634,7 +1629,7 @@ export function expandAllCallNodeDescendants( }); const expandedCallNodePaths = [...descendants].map((callNodeIndex) => - getCallNodePathFromIndex(callNodeIndex, callNodeInfo.callNodeTable) + callNodeInfo.getCallNodePathFromIndex(callNodeIndex) ); dispatch(changeExpandedCallNodes(threadsKey, expandedCallNodePaths)); }; @@ -1878,13 +1873,13 @@ export function addTransformToStack( const transformedThread = threadSelectors.getRangeAndTransformFilteredThread(getState()); - const { callNodeTable } = threadSelectors.getCallNodeInfo(getState()); + const callNodeInfo = threadSelectors.getCallNodeInfo(getState()); dispatch({ type: 'ADD_TRANSFORM_TO_STACK', threadsKey, transform, transformedThread, - callNodeTable, + callNodeInfo, }); sendAnalytics({ hitType: 'event', @@ -2041,10 +2036,11 @@ export function handleCallNodeTransformShortcut( } const threadSelectors = getThreadSelectorsFromThreadsKey(threadsKey); const unfilteredThread = threadSelectors.getThread(getState()); - const { callNodeTable } = threadSelectors.getCallNodeInfo(getState()); + const callNodeInfo = threadSelectors.getCallNodeInfo(getState()); + const callNodeTable = callNodeInfo.getCallNodeTable(); const implementation = getImplementationFilter(getState()); const inverted = getInvertCallstack(getState()); - const callNodePath = getCallNodePathFromIndex(callNodeIndex, callNodeTable); + const callNodePath = callNodeInfo.getCallNodePathFromIndex(callNodeIndex); const funcIndex = callNodeTable.func[callNodeIndex]; const category = callNodeTable.category[callNodeIndex]; diff --git a/src/components/calltree/CallTree.js b/src/components/calltree/CallTree.js index 2b6feceb95..4ad5c7110d 100644 --- a/src/components/calltree/CallTree.js +++ b/src/components/calltree/CallTree.js @@ -9,7 +9,6 @@ import explicitConnect from 'firefox-profiler/utils/connect'; import { TreeView } from 'firefox-profiler/components/shared/TreeView'; import { CallTreeEmptyReasons } from './CallTreeEmptyReasons'; import { Icon } from 'firefox-profiler/components/shared/Icon'; -import { getCallNodePathFromIndex } from 'firefox-profiler/profile-logic/profile-data'; import { getInvertCallstack, getImplementationFilter, @@ -246,7 +245,7 @@ class CallTreeImpl extends PureComponent { const { callNodeInfo, threadsKey, changeSelectedCallNode } = this.props; changeSelectedCallNode( threadsKey, - getCallNodePathFromIndex(newSelectedCallNode, callNodeInfo.callNodeTable), + callNodeInfo.getCallNodePathFromIndex(newSelectedCallNode), context ); }; @@ -255,7 +254,7 @@ class CallTreeImpl extends PureComponent { const { callNodeInfo, threadsKey, changeRightClickedCallNode } = this.props; changeRightClickedCallNode( threadsKey, - getCallNodePathFromIndex(newSelectedCallNode, callNodeInfo.callNodeTable) + callNodeInfo.getCallNodePathFromIndex(newSelectedCallNode) ); }; @@ -266,7 +265,7 @@ class CallTreeImpl extends PureComponent { changeExpandedCallNodes( threadsKey, newExpandedCallNodeIndexes.map((callNodeIndex) => - getCallNodePathFromIndex(callNodeIndex, callNodeInfo.callNodeTable) + callNodeInfo.getCallNodePathFromIndex(callNodeIndex) ) ); }; @@ -301,7 +300,7 @@ class CallTreeImpl extends PureComponent { tree, expandedCallNodeIndexes, selectedCallNodeIndex, - callNodeInfo: { callNodeTable }, + callNodeInfo, categories, } = this.props; @@ -321,6 +320,7 @@ class CallTreeImpl extends PureComponent { // This tree is empty. return; } + const callNodeTable = callNodeInfo.getCallNodeTable(); newExpandedCallNodeIndexes.push(currentCallNodeIndex); for (let i = 0; i < maxInterestingDepth; i++) { const children = tree.getChildren(currentCallNodeIndex); diff --git a/src/components/flame-graph/Canvas.js b/src/components/flame-graph/Canvas.js index 445479b554..22b1c54226 100644 --- a/src/components/flame-graph/Canvas.js +++ b/src/components/flame-graph/Canvas.js @@ -154,16 +154,14 @@ class FlameGraphCanvasImpl extends React.PureComponent { } _scrollSelectionIntoView = () => { - const { - selectedCallNodeIndex, - maxStackDepthPlusOne, - callNodeInfo: { callNodeTable }, - } = this.props; + const { selectedCallNodeIndex, maxStackDepthPlusOne, callNodeInfo } = + this.props; if (selectedCallNodeIndex === null) { return; } + const callNodeTable = callNodeInfo.getCallNodeTable(); const depth = callNodeTable.depth[selectedCallNodeIndex]; const y = (maxStackDepthPlusOne - depth - 1) * ROW_HEIGHT; @@ -185,7 +183,7 @@ class FlameGraphCanvasImpl extends React.PureComponent { const { thread, flameGraphTiming, - callNodeInfo: { callNodeTable }, + callNodeInfo, stackFrameHeight, maxStackDepthPlusOne, rightClickedCallNodeIndex, @@ -237,6 +235,8 @@ class FlameGraphCanvasImpl extends React.PureComponent { fastFillStyle.set('#ffffff'); ctx.fillRect(0, 0, deviceContainerWidth, deviceContainerHeight); + const callNodeTable = callNodeInfo.getCallNodeTable(); + const startDepth = Math.floor( maxStackDepthPlusOne - viewportBottom / stackFrameHeight ); diff --git a/src/components/flame-graph/FlameGraph.js b/src/components/flame-graph/FlameGraph.js index 64210dc728..35769154e3 100644 --- a/src/components/flame-graph/FlameGraph.js +++ b/src/components/flame-graph/FlameGraph.js @@ -24,7 +24,6 @@ import { getInvertCallstack, } from '../../selectors/url-state'; import { ContextMenuTrigger } from 'firefox-profiler/components/shared/ContextMenuTrigger'; -import { getCallNodePathFromIndex } from 'firefox-profiler/profile-logic/profile-data'; import { changeSelectedCallNode, changeRightClickedCallNode, @@ -117,7 +116,7 @@ class FlameGraphImpl extends React.PureComponent { const { callNodeInfo, threadsKey, changeSelectedCallNode } = this.props; changeSelectedCallNode( threadsKey, - getCallNodePathFromIndex(callNodeIndex, callNodeInfo.callNodeTable) + callNodeInfo.getCallNodePathFromIndex(callNodeIndex) ); }; @@ -127,7 +126,7 @@ class FlameGraphImpl extends React.PureComponent { const { callNodeInfo, threadsKey, changeRightClickedCallNode } = this.props; changeRightClickedCallNode( threadsKey, - getCallNodePathFromIndex(callNodeIndex, callNodeInfo.callNodeTable) + callNodeInfo.getCallNodePathFromIndex(callNodeIndex) ); }; @@ -160,11 +159,9 @@ class FlameGraphImpl extends React.PureComponent { * Is the box for this call node wide enough to be selected? */ _wideEnough = (callNodeIndex: IndexIntoCallNodeTable): boolean => { - const { - flameGraphTiming, - callNodeInfo: { callNodeTable }, - } = this.props; + const { flameGraphTiming, callNodeInfo } = this.props; + const callNodeTable = callNodeInfo.getCallNodeTable(); const depth = callNodeTable.depth[callNodeIndex]; const row = flameGraphTiming[depth]; const columnIndex = row.callNode.indexOf(callNodeIndex); @@ -185,13 +182,11 @@ class FlameGraphImpl extends React.PureComponent { startingCallNodeIndex: IndexIntoCallNodeTable, direction: 1 | -1 ): IndexIntoCallNodeTable | void => { - const { - flameGraphTiming, - callNodeInfo: { callNodeTable }, - } = this.props; + const { flameGraphTiming, callNodeInfo } = this.props; let callNodeIndex = startingCallNodeIndex; + const callNodeTable = callNodeInfo.getCallNodeTable(); const depth = callNodeTable.depth[callNodeIndex]; const row = flameGraphTiming[depth]; let columnIndex = row.callNode.indexOf(callNodeIndex); @@ -216,12 +211,13 @@ class FlameGraphImpl extends React.PureComponent { const { threadsKey, callTree, - callNodeInfo: { callNodeTable }, + callNodeInfo, selectedCallNodeIndex, rightClickedCallNodeIndex, changeSelectedCallNode, handleCallNodeTransformShortcut, } = this.props; + const callNodeTable = callNodeInfo.getCallNodeTable(); if ( // Please do not forget to update the switch/case below if changing the array to allow more keys. @@ -231,7 +227,7 @@ class FlameGraphImpl extends React.PureComponent { // Just select the "root" node if we've got no prior selection. changeSelectedCallNode( threadsKey, - getCallNodePathFromIndex(0, callNodeTable) + callNodeInfo.getCallNodePathFromIndex(0) ); return; } @@ -242,7 +238,7 @@ class FlameGraphImpl extends React.PureComponent { if (prefix !== -1) { changeSelectedCallNode( threadsKey, - getCallNodePathFromIndex(prefix, callNodeTable) + callNodeInfo.getCallNodePathFromIndex(prefix) ); } break; @@ -257,7 +253,7 @@ class FlameGraphImpl extends React.PureComponent { if (callNodeIndex !== undefined && this._wideEnough(callNodeIndex)) { changeSelectedCallNode( threadsKey, - getCallNodePathFromIndex(callNodeIndex, callNodeTable) + callNodeInfo.getCallNodePathFromIndex(callNodeIndex) ); } break; @@ -272,7 +268,7 @@ class FlameGraphImpl extends React.PureComponent { if (callNodeIndex !== undefined) { changeSelectedCallNode( threadsKey, - getCallNodePathFromIndex(callNodeIndex, callNodeTable) + callNodeInfo.getCallNodePathFromIndex(callNodeIndex) ); } break; @@ -306,11 +302,8 @@ class FlameGraphImpl extends React.PureComponent { _onCopy = (event: ClipboardEvent) => { if (document.activeElement === this._viewport) { event.preventDefault(); - const { - callNodeInfo: { callNodeTable }, - selectedCallNodeIndex, - thread, - } = this.props; + const { callNodeInfo, selectedCallNodeIndex, thread } = this.props; + const callNodeTable = callNodeInfo.getCallNodeTable(); if (selectedCallNodeIndex !== null) { const funcIndex = callNodeTable.func[selectedCallNodeIndex]; const funcName = thread.stringTable.getString( diff --git a/src/components/shared/CallNodeContextMenu.js b/src/components/shared/CallNodeContextMenu.js index d62b303e37..2a9dbbc1bb 100644 --- a/src/components/shared/CallNodeContextMenu.js +++ b/src/components/shared/CallNodeContextMenu.js @@ -18,7 +18,6 @@ import { getFunctionName } from 'firefox-profiler/profile-logic/function-info'; import { getBottomBoxInfoForCallNode, getOriginAnnotationForFunc, - getCallNodePathFromIndex, } from 'firefox-profiler/profile-logic/profile-data'; import { getCategories } from 'firefox-profiler/selectors'; @@ -136,9 +135,10 @@ class CallNodeContextMenuImpl extends React.PureComponent { const { callNodeIndex, thread: { stringTable, funcTable }, - callNodeInfo: { callNodeTable }, + callNodeInfo, } = rightClickedCallNodeInfo; + const callNodeTable = callNodeInfo.getCallNodeTable(); const funcIndex = callNodeTable.func[callNodeIndex]; const isJS = funcTable.isJS[funcIndex]; const stringIndex = funcTable.name[funcIndex]; @@ -173,9 +173,10 @@ class CallNodeContextMenuImpl extends React.PureComponent { const { callNodeIndex, thread: { stringTable, funcTable }, - callNodeInfo: { callNodeTable }, + callNodeInfo, } = rightClickedCallNodeInfo; + const callNodeTable = callNodeInfo.getCallNodeTable(); const funcIndex = callNodeTable.func[callNodeIndex]; const stringIndex = funcTable.fileName[funcIndex]; if (stringIndex === null) { @@ -223,13 +224,12 @@ class CallNodeContextMenuImpl extends React.PureComponent { const { callNodeIndex, thread: { funcTable, resourceTable, stringTable }, - callNodeInfo: { callNodeTable }, + callNodeInfo, } = rightClickedCallNodeInfo; - const callPath = getCallNodePathFromIndex( - callNodeIndex, - callNodeTable - ).reverse(); + const callPath = callNodeInfo + .getCallNodePathFromIndex(callNodeIndex) + .reverse(); const stack = callPath .map((funcIndex) => { @@ -300,14 +300,10 @@ class CallNodeContextMenuImpl extends React.PureComponent { ); } - const { - threadsKey, - callNodePath, - thread, - callNodeIndex, - callNodeInfo: { callNodeTable }, - } = rightClickedCallNodeInfo; + const { threadsKey, callNodePath, thread, callNodeIndex, callNodeInfo } = + rightClickedCallNodeInfo; const selectedFunc = callNodePath[callNodePath.length - 1]; + const callNodeTable = callNodeInfo.getCallNodeTable(); const category = callNodeTable.category[callNodeIndex]; switch (type) { case 'focus-subtree': @@ -489,9 +485,10 @@ class CallNodeContextMenuImpl extends React.PureComponent { const { callNodeIndex, thread: { funcTable }, - callNodeInfo: { callNodeTable }, + callNodeInfo, } = rightClickedCallNodeInfo; + const callNodeTable = callNodeInfo.getCallNodeTable(); const categoryIndex = callNodeTable.category[callNodeIndex]; const funcIndex = callNodeTable.func[callNodeIndex]; const isJS = funcTable.isJS[funcIndex]; diff --git a/src/components/shared/thread/StackGraph.js b/src/components/shared/thread/StackGraph.js index 381546da6b..b605584edd 100644 --- a/src/components/shared/thread/StackGraph.js +++ b/src/components/shared/thread/StackGraph.js @@ -43,8 +43,8 @@ export class ThreadStackGraph extends PureComponent { if (callNodeIndex === null) { return null; } - - return callNodeInfo.callNodeTable.depth[callNodeIndex]; + const callNodeTable = callNodeInfo.getCallNodeTable(); + return callNodeTable.depth[callNodeIndex]; }; render() { @@ -60,7 +60,7 @@ export class ThreadStackGraph extends PureComponent { trackName, onSampleClick, } = this.props; - const { callNodeTable } = callNodeInfo; + const callNodeTable = callNodeInfo.getCallNodeTable(); let maxDepth = 0; for (let i = 0; i < callNodeTable.depth.length; i++) { diff --git a/src/components/stack-chart/Canvas.js b/src/components/stack-chart/Canvas.js index e4cc672fab..da20f1acce 100644 --- a/src/components/stack-chart/Canvas.js +++ b/src/components/stack-chart/Canvas.js @@ -122,15 +122,13 @@ class StackChartCanvasImpl extends React.PureComponent { } _scrollSelectionIntoView = () => { - const { - selectedCallNodeIndex, - callNodeInfo: { callNodeTable }, - } = this.props; + const { selectedCallNodeIndex, callNodeInfo } = this.props; if (selectedCallNodeIndex === null) { return; } + const callNodeTable = callNodeInfo.getCallNodeTable(); const depth = callNodeTable.depth[selectedCallNodeIndex]; const y = depth * ROW_CSS_PIXELS_HEIGHT; @@ -166,7 +164,7 @@ class StackChartCanvasImpl extends React.PureComponent { stackFrameHeight, selectedCallNodeIndex, categories, - callNodeInfo: { callNodeTable }, + callNodeInfo, getMarker, marginLeft, viewport: { @@ -264,6 +262,8 @@ class StackChartCanvasImpl extends React.PureComponent { categoryForUserTiming = 0; } + const callNodeTable = callNodeInfo.getCallNodeTable(); + // Only draw the stack frames that are vertically within view. for (let depth = startDepth; depth < endDepth; depth++) { // Get the timing information for a row of stack frames. diff --git a/src/components/stack-chart/index.js b/src/components/stack-chart/index.js index 368fe0d699..90120e458e 100644 --- a/src/components/stack-chart/index.js +++ b/src/components/stack-chart/index.js @@ -38,10 +38,7 @@ import { changeMouseTimePosition, } from '../../actions/profile-view'; -import { - getCallNodePathFromIndex, - getBottomBoxInfoForCallNode, -} from '../../profile-logic/profile-data'; +import { getBottomBoxInfoForCallNode } from '../../profile-logic/profile-data'; import type { Thread, @@ -122,7 +119,7 @@ class StackChartImpl extends React.PureComponent { const { callNodeInfo, threadsKey, changeSelectedCallNode } = this.props; changeSelectedCallNode( threadsKey, - getCallNodePathFromIndex(callNodeIndex, callNodeInfo.callNodeTable) + callNodeInfo.getCallNodePathFromIndex(callNodeIndex) ); }; @@ -131,7 +128,7 @@ class StackChartImpl extends React.PureComponent { changeRightClickedCallNode( threadsKey, - getCallNodePathFromIndex(callNodeIndex, callNodeInfo.callNodeTable) + callNodeInfo.getCallNodePathFromIndex(callNodeIndex) ); }; @@ -182,12 +179,9 @@ class StackChartImpl extends React.PureComponent { _onCopy = (event: ClipboardEvent) => { if (document.activeElement === this._viewport) { event.preventDefault(); - const { - callNodeInfo: { callNodeTable }, - selectedCallNodeIndex, - thread, - } = this.props; + const { callNodeInfo, selectedCallNodeIndex, thread } = this.props; if (selectedCallNodeIndex !== null) { + const callNodeTable = callNodeInfo.getCallNodeTable(); const funcIndex = callNodeTable.func[selectedCallNodeIndex]; const funcName = thread.stringTable.getString( thread.funcTable.name[funcIndex] diff --git a/src/components/tooltip/CallNode.js b/src/components/tooltip/CallNode.js index 79effb53a9..8ae5979040 100644 --- a/src/components/tooltip/CallNode.js +++ b/src/components/tooltip/CallNode.js @@ -430,9 +430,10 @@ export class TooltipCallNode extends React.PureComponent { timings, callTreeSummaryStrategy, innerWindowIDToPageMap, - callNodeInfo: { callNodeTable }, + callNodeInfo, displayStackType, } = this.props; + const callNodeTable = callNodeInfo.getCallNodeTable(); const categoryIndex = callNodeTable.category[callNodeIndex]; const categoryColor = categories[categoryIndex].color; const subcategoryIndex = callNodeTable.subcategory[callNodeIndex]; diff --git a/src/profile-logic/address-timings.js b/src/profile-logic/address-timings.js index d409e62a50..3ff041777b 100644 --- a/src/profile-logic/address-timings.js +++ b/src/profile-logic/address-timings.js @@ -202,7 +202,7 @@ export function getStackAddressInfoForCallNode( callNodeInfo: CallNodeInfo, nativeSymbol: IndexIntoNativeSymbolTable ): StackAddressInfo { - return callNodeInfo.isInverted + return callNodeInfo.isInverted() ? getStackAddressInfoForCallNodeInverted( stackTable, frameTable, @@ -347,9 +347,11 @@ export function getStackAddressInfoForCallNodeNonInverted( stackTable: StackTable, frameTable: FrameTable, callNodeIndex: IndexIntoCallNodeTable, - { stackIndexToCallNodeIndex }: CallNodeInfo, + callNodeInfo: CallNodeInfo, nativeSymbol: IndexIntoNativeSymbolTable ): StackAddressInfo { + const stackIndexToCallNodeIndex = callNodeInfo.getStackIndexToCallNodeIndex(); + // "self address" == "the address which a stack's self time is contributed to" const callNodeSelfAddressForAllStacks = []; // "total addresses" == "the set of addresses whose total time this stack contributes to" @@ -426,12 +428,12 @@ export function getStackAddressInfoForCallNodeInverted( callNodeInfo: CallNodeInfo, nativeSymbol: IndexIntoNativeSymbolTable ): StackAddressInfo { - const invertedCallNodeTable = callNodeInfo.callNodeTable; + const invertedCallNodeTable = callNodeInfo.getCallNodeTable(); const depth = invertedCallNodeTable.depth[callNodeIndex]; const endIndex = invertedCallNodeTable.subtreeRangeEnd[callNodeIndex]; const callNodeIsRootOfInvertedTree = invertedCallNodeTable.prefix[callNodeIndex] === -1; - const { stackIndexToCallNodeIndex } = callNodeInfo; + const stackIndexToCallNodeIndex = callNodeInfo.getStackIndexToCallNodeIndex(); const stackTablePrefixCol = stackTable.prefix; // "self address" == "the address which a stack's self time is contributed to" diff --git a/src/profile-logic/call-node-info.js b/src/profile-logic/call-node-info.js new file mode 100644 index 0000000000..3d85522a0a --- /dev/null +++ b/src/profile-logic/call-node-info.js @@ -0,0 +1,180 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * 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/. */ + +// @flow + +import { hashPath } from 'firefox-profiler/utils/path'; + +import type { + IndexIntoFuncTable, + CallNodeInfo, + CallNodeTable, + CallNodePath, + IndexIntoCallNodeTable, +} from 'firefox-profiler/types'; + +/** + * The implementation of the CallNodeInfo interface. + */ +export class CallNodeInfoImpl implements CallNodeInfo { + // If true, call node indexes describe nodes in the inverted call tree. + _isInverted: boolean; + + // The call node table. + _callNodeTable: CallNodeTable; + + // The mapping of stack index to corresponding call node index. + _stackIndexToCallNodeIndex: Int32Array; + + // This is a Map. This map speeds up + // the look-up process by caching every CallNodePath we handle which avoids + // looking up parents again and again. + _cache: Map = new Map(); + + constructor( + callNodeTable: CallNodeTable, + stackIndexToCallNodeIndex: Int32Array, + isInverted: boolean + ) { + this._callNodeTable = callNodeTable; + this._stackIndexToCallNodeIndex = stackIndexToCallNodeIndex; + this._isInverted = isInverted; + } + + isInverted(): boolean { + return this._isInverted; + } + + getCallNodeTable(): CallNodeTable { + return this._callNodeTable; + } + + getStackIndexToCallNodeIndex(): Int32Array { + return this._stackIndexToCallNodeIndex; + } + + getCallNodePathFromIndex( + callNodeIndex: IndexIntoCallNodeTable | null + ): CallNodePath { + if (callNodeIndex === null || callNodeIndex === -1) { + return []; + } + + const callNodePath = []; + let cni = callNodeIndex; + while (cni !== -1) { + callNodePath.push(this._callNodeTable.func[cni]); + cni = this._callNodeTable.prefix[cni]; + } + callNodePath.reverse(); + return callNodePath; + } + + getCallNodeIndexFromPath( + callNodePath: CallNodePath + ): IndexIntoCallNodeTable | null { + const cache = this._cache; + const hashFullPath = hashPath(callNodePath); + const result = cache.get(hashFullPath); + if (result !== undefined) { + // The cache already has the result for the full path. + return result; + } + + // This array serves as a map and stores the hashes of callNodePath's + // parents to speed up the algorithm. First we'll follow the tree from the + // bottom towards the top, pushing hashes as we compute them, and then we'll + // move back towards the bottom popping hashes from this array. + const sliceHashes = [hashFullPath]; + + // Step 1: find whether we already computed the index for one of the path's + // parents, starting from the closest parent and looping towards the "top" of + // the tree. + // If we find it for one of the parents, we'll be able to start at this point + // in the following look up. + let i = callNodePath.length; + let index; + while (--i > 0) { + // Looking up each parent for this call node, starting from the deepest node. + // If we find a parent this makes it possible to start the look up from this location. + const subPath = callNodePath.slice(0, i); + const hash = hashPath(subPath); + index = cache.get(hash); + if (index !== undefined) { + // Yay, we already have the result for a parent! + break; + } + // Cache the hashed value because we'll need it later, after resolving this path. + // Note we don't add the hash if we found the parent in the cache, so the + // last added element here will accordingly be the first popped in the next + // algorithm. + sliceHashes.push(hash); + } + + // Step 2: look for the requested path using the call node table, starting at + // the parent we already know if we found one, and looping down the tree. + // We're contributing to the cache at the same time. + + // `index` is undefined if no parent was found in the cache. In that case we + // start from the start, and use `-1` which is the prefix we use to indicate + // the root node. + if (index === undefined) { + // assert(i === 0); + index = -1; + } + + while (i < callNodePath.length) { + // Resolving the index for subpath `callNodePath.slice(0, i+1)` given we + // know the index for the subpath `callNodePath.slice(0, i)` (its parent). + const func = callNodePath[i]; + const nextNodeIndex = this.getCallNodeIndexFromParentAndFunc(index, func); + + // We couldn't find this path into the call node table. This shouldn't + // normally happen. + if (nextNodeIndex === null) { + return null; + } + + // Contributing to the shared cache + const hash = sliceHashes.pop(); + cache.set(hash, nextNodeIndex); + + index = nextNodeIndex; + i++; + } + + return index < 0 ? null : index; + } + + getCallNodeIndexFromParentAndFunc( + parent: IndexIntoCallNodeTable | -1, + func: IndexIntoFuncTable + ): IndexIntoCallNodeTable | null { + const callNodeTable = this._callNodeTable; + if (parent === -1) { + if (callNodeTable.length === 0) { + return null; + } + } else if (callNodeTable.subtreeRangeEnd[parent] === parent + 1) { + // parent has no children. + return null; + } + // Node children always come after their parents in the call node table, + // that's why we start looping at `parent + 1`. + // Note that because the root parent is `-1`, we correctly start at `0` when + // we look for a root. + const firstChild = parent + 1; + for ( + let callNodeIndex = firstChild; + callNodeIndex !== -1; + callNodeIndex = callNodeTable.nextSibling[callNodeIndex] + ) { + if (callNodeTable.func[callNodeIndex] === func) { + return callNodeIndex; + } + } + + return null; + } +} diff --git a/src/profile-logic/call-tree.js b/src/profile-logic/call-tree.js index af553d6686..6ac2174eac 100644 --- a/src/profile-logic/call-tree.js +++ b/src/profile-logic/call-tree.js @@ -10,7 +10,6 @@ import { getOriginAnnotationForFunc, getCategoryPairLabel, getBottomBoxInfoForCallNode, - getCallNodePathFromIndex, } from './profile-data'; import { resourceTypes } from './data-structures'; import { getFunctionName } from './function-info'; @@ -98,7 +97,7 @@ export class CallTree { ) { this._categories = categories; this._callNodeInfo = callNodeInfo; - this._callNodeTable = callNodeInfo.callNodeTable; + this._callNodeTable = callNodeInfo.getCallNodeTable(); this._callNodeSummary = callNodeSummary; this._callNodeHasChildren = callNodeHasChildren; this._thread = thread; @@ -415,7 +414,7 @@ export class CallTree { } } - return getCallNodePathFromIndex(maxNode, this._callNodeTable); + return this._callNodeInfo.getCallNodePathFromIndex(maxNode); } } @@ -515,9 +514,11 @@ function _getStackSelf( export function computeCallTreeTimings( samples: SamplesLikeTable, sampleIndexToCallNodeIndex: Array, - { callNodeTable }: CallNodeInfo, + callNodeInfo: CallNodeInfo, invertCallstack: boolean ): CallTreeTimings { + const callNodeTable = callNodeInfo.getCallNodeTable(); + // Inverted trees need a different method for computing the timing. const { callNodeSelf, callNodeLeaf } = invertCallstack ? _getInvertedStackSelf(samples, callNodeTable, sampleIndexToCallNodeIndex) @@ -686,10 +687,13 @@ export function extractSamplesLikeTable( */ export function computeTracedTiming( samples: SamplesLikeTable, - { callNodeTable, stackIndexToCallNodeIndex }: CallNodeInfo, + callNodeInfo: CallNodeInfo, interval: Milliseconds, invertCallstack: boolean ): TracedTiming | null { + const callNodeTable = callNodeInfo.getCallNodeTable(); + const stackIndexToCallNodeIndex = callNodeInfo.getStackIndexToCallNodeIndex(); + if (samples.weightType !== 'samples' || samples.weight) { // Only compute for the samples weight types that have no weights. If a samples // table has weights then it's a diff profile. Currently, we aren't calculating diff --git a/src/profile-logic/line-timings.js b/src/profile-logic/line-timings.js index 293f26968e..91ab961117 100644 --- a/src/profile-logic/line-timings.js +++ b/src/profile-logic/line-timings.js @@ -125,7 +125,7 @@ export function getStackLineInfoForCallNode( callNodeIndex: IndexIntoCallNodeTable, callNodeInfo: CallNodeInfo ): StackLineInfo { - return callNodeInfo.isInverted + return callNodeInfo.isInverted() ? getStackLineInfoForCallNodeInverted( stackTable, frameTable, @@ -207,8 +207,10 @@ export function getStackLineInfoForCallNodeNonInverted( stackTable: StackTable, frameTable: FrameTable, callNodeIndex: IndexIntoCallNodeTable, - { stackIndexToCallNodeIndex }: CallNodeInfo + callNodeInfo: CallNodeInfo ): StackLineInfo { + const stackIndexToCallNodeIndex = callNodeInfo.getStackIndexToCallNodeIndex(); + // "self line" == "the line which a stack's self time is contributed to" const callNodeSelfLineForAllStacks = []; // "total lines" == "the set of lines whose total time this stack contributes to" @@ -281,12 +283,12 @@ export function getStackLineInfoForCallNodeInverted( callNodeIndex: IndexIntoCallNodeTable, callNodeInfo: CallNodeInfo ): StackLineInfo { - const invertedCallNodeTable = callNodeInfo.callNodeTable; + const invertedCallNodeTable = callNodeInfo.getCallNodeTable(); const depth = invertedCallNodeTable.depth[callNodeIndex]; const endIndex = invertedCallNodeTable.subtreeRangeEnd[callNodeIndex]; const callNodeIsRootOfInvertedTree = invertedCallNodeTable.prefix[callNodeIndex] === -1; - const { stackIndexToCallNodeIndex } = callNodeInfo; + const stackIndexToCallNodeIndex = callNodeInfo.getStackIndexToCallNodeIndex(); const stackTablePrefixCol = stackTable.prefix; // "self line" == "the line which a stack's self time is contributed to" diff --git a/src/profile-logic/profile-data.js b/src/profile-logic/profile-data.js index eb216df9e6..6a8226a40f 100644 --- a/src/profile-logic/profile-data.js +++ b/src/profile-logic/profile-data.js @@ -16,6 +16,7 @@ import { shallowCloneFrameTable, shallowCloneFuncTable, } from './data-structures'; +import { CallNodeInfoImpl } from './call-node-info'; import { INSTANT, INTERVAL, @@ -23,7 +24,6 @@ import { INTERVAL_END, } from 'firefox-profiler/app-logic/constants'; import { timeCode } from 'firefox-profiler/utils/time-code'; -import { hashPath } from 'firefox-profiler/utils/path'; import { bisectionRight, bisectionLeft } from 'firefox-profiler/utils/bisect'; import { parseFileNameFromSymbolication } from 'firefox-profiler/utils/special-paths'; import { @@ -91,20 +91,43 @@ import type { UniqueStringArray } from 'firefox-profiler/utils/unique-string-arr */ /** - * Generate the CallNodeInfo which contains the CallNodeTable, and a map to convert - * an IndexIntoStackTable to a IndexIntoCallNodeTable. This function runs through - * a stackTable, and de-duplicates stacks that have frames that point to the same - * function. + * Generate the non-inverted CallNodeInfo for a thread. + */ +export function getCallNodeInfo( + stackTable: StackTable, + frameTable: FrameTable, + funcTable: FuncTable, + defaultCategory: IndexIntoCategoryList +): CallNodeInfo { + const { callNodeTable, stackIndexToCallNodeIndex } = computeCallNodeTable( + stackTable, + frameTable, + funcTable, + defaultCategory + ); + return new CallNodeInfoImpl(callNodeTable, stackIndexToCallNodeIndex, false); +} + +type CallNodeTableAndStackMap = { + callNodeTable: CallNodeTable, + // IndexIntoStackTable -> IndexIntoCallNodeTable + stackIndexToCallNodeIndex: Int32Array, +}; + +/** + * Generate the CallNodeTable, and a map to convert an IndexIntoStackTable to a + * IndexIntoCallNodeTable. This function runs through a stackTable, and + * de-duplicates stacks that have frames that point to the same function. * * See `src/types/profile-derived.js` for the type definitions. * See `docs-developer/call-trees.md` for a detailed explanation of CallNodes. */ -export function getCallNodeInfo( +export function computeCallNodeTable( stackTable: StackTable, frameTable: FrameTable, funcTable: FuncTable, defaultCategory: IndexIntoCategoryList -): CallNodeInfo { +): CallNodeTableAndStackMap { return timeCode('getCallNodeInfo', () => { const stackIndexToCallNodeIndex = new Int32Array(stackTable.length); @@ -254,7 +277,7 @@ export function getCallNodeInfo( } stackIndexToCallNodeIndex[stackIndex] = callNodeIndex; } - return _createCallNodeInfoFromUnorderedComponents( + return _createCallNodeTableFromUnorderedComponents( prefix, firstChild, nextSibling, @@ -270,8 +293,8 @@ export function getCallNodeInfo( } /** - * Create a CallNodeInfo with an ordered call node table based on the pieces of - * an unordered call node table. + * Create a CallNodeTableAndStackMap with an ordered call node table based on + * the pieces of an unordered call node table. * * The order of siblings is maintained. * If a node A has children, its first child B directly follows A. @@ -279,7 +302,7 @@ export function getCallNodeInfo( * next sibling of the closest ancestor which has a next sibling. * This means that any node and all its descendants are laid out contiguously. */ -function _createCallNodeInfoFromUnorderedComponents( +function _createCallNodeTableFromUnorderedComponents( prefix: Array, firstChild: Array, nextSibling: Array, @@ -290,13 +313,12 @@ function _createCallNodeInfoFromUnorderedComponents( sourceFramesInlinedIntoSymbol: Array, length: number, stackIndexToCallNodeIndex: Int32Array -): CallNodeInfo { +): CallNodeTableAndStackMap { return timeCode('createCallNodeInfoFromUnorderedComponents', () => { if (length === 0) { return { callNodeTable: getEmptyCallNodeTable(), stackIndexToCallNodeIndex: new Int32Array(0), - isInverted: false, }; } @@ -392,11 +414,13 @@ function _createCallNodeInfoFromUnorderedComponents( stackIndexToCallNodeIndex: stackIndexToCallNodeIndex.map( (oldIndex) => oldIndexToNewIndex[oldIndex] ), - isInverted: false, }; }); } +/** + * Generate the inverted CallNodeInfo for a thread. + */ export function getInvertedCallNodeInfo( thread: Thread, defaultCategory: IndexIntoCategoryList @@ -411,7 +435,7 @@ export function getInvertedCallNodeInfo( const { callNodeTable, stackIndexToCallNodeIndex: invertedStackIndexToCallNodeIndex, - } = getCallNodeInfo( + } = computeCallNodeTable( invertedThread.stackTable, invertedThread.frameTable, invertedThread.funcTable, @@ -442,11 +466,11 @@ export function getInvertedCallNodeInfo( invertedStackIndexToCallNodeIndex[invertedStackIndex]; } } - return { + return new CallNodeInfoImpl( callNodeTable, - stackIndexToCallNodeIndex: nonInvertedStackIndexToCallNodeIndex, - isInverted: true, - }; + nonInvertedStackIndexToCallNodeIndex, + true + ); } // Given a stack index `needleStack` and a call node in the inverted tree @@ -677,7 +701,7 @@ function mapCallNodeSelectedStatesToSamples( * from the same subtree (in the call tree) "clump together" in the graph. */ export function getSamplesSelectedStates( - callNodeTable: CallNodeTable, + callNodeInfo: CallNodeInfo, sampleCallNodes: Array, activeTabFilteredCallNodes: Array, selectedCallNodeIndex: IndexIntoCallNodeTable | null @@ -689,6 +713,7 @@ export function getSamplesSelectedStates( ); } + const callNodeTable = callNodeInfo.getCallNodeTable(); return mapCallNodeSelectedStatesToSamples( sampleCallNodes, activeTabFilteredCallNodes, @@ -761,7 +786,7 @@ export function getTimingsForPath( displayImplementation: boolean ) { return getTimingsForCallNodeIndex( - getCallNodeIndexFromPath(needlePath, callNodeInfo.callNodeTable), + callNodeInfo.getCallNodeIndexFromPath(needlePath), callNodeInfo, interval, isInvertedTree, @@ -785,7 +810,7 @@ export function getTimingsForPath( */ export function getTimingsForCallNodeIndex( needleNodeIndex: IndexIntoCallNodeTable | null, - { callNodeTable, stackIndexToCallNodeIndex }: CallNodeInfo, + callNodeInfo: CallNodeInfo, interval: Milliseconds, isInvertedTree: boolean, thread: Thread, @@ -978,6 +1003,9 @@ export function getTimingsForCallNodeIndex( return { forPath: pathTimings, rootTime }; } + const callNodeTable = callNodeInfo.getCallNodeTable(); + const stackIndexToCallNodeIndex = callNodeInfo.getStackIndexToCallNodeIndex(); + const needleDescendantsEndIndex = callNodeTable.subtreeRangeEnd[needleNodeIndex]; @@ -1940,168 +1968,6 @@ export function processEventDelays( }; } -// --------------- CallNodePath and CallNodeIndex manipulations --------------- - -// Returns a list of CallNodeIndex from CallNodePaths. This function uses a map -// to speed up the look-up process. -export function getCallNodeIndicesFromPaths( - callNodePaths: CallNodePath[], - callNodeTable: CallNodeTable -): Array { - // This is a Map. This map speeds up - // the look-up process by caching every CallNodePath we handle which avoids - // looking up parents again and again. - const cache = new Map(); - return callNodePaths.map((path) => - _getCallNodeIndexFromPathWithCache(path, callNodeTable, cache) - ); -} - -// Returns a CallNodeIndex from a CallNodePath, using and contributing to the -// cache parameter. -function _getCallNodeIndexFromPathWithCache( - callNodePath: CallNodePath, - callNodeTable: CallNodeTable, - cache: Map -): IndexIntoCallNodeTable | null { - const hashFullPath = hashPath(callNodePath); - const result = cache.get(hashFullPath); - if (result !== undefined) { - // The cache already has the result for the full path. - return result; - } - - // This array serves as a map and stores the hashes of callNodePath's - // parents to speed up the algorithm. First we'll follow the tree from the - // bottom towards the top, pushing hashes as we compute them, and then we'll - // move back towards the bottom popping hashes from this array. - const sliceHashes = [hashFullPath]; - - // Step 1: find whether we already computed the index for one of the path's - // parents, starting from the closest parent and looping towards the "top" of - // the tree. - // If we find it for one of the parents, we'll be able to start at this point - // in the following look up. - let i = callNodePath.length; - let index; - while (--i > 0) { - // Looking up each parent for this call node, starting from the deepest node. - // If we find a parent this makes it possible to start the look up from this location. - const subPath = callNodePath.slice(0, i); - const hash = hashPath(subPath); - index = cache.get(hash); - if (index !== undefined) { - // Yay, we already have the result for a parent! - break; - } - // Cache the hashed value because we'll need it later, after resolving this path. - // Note we don't add the hash if we found the parent in the cache, so the - // last added element here will accordingly be the first popped in the next - // algorithm. - sliceHashes.push(hash); - } - - // Step 2: look for the requested path using the call node table, starting at - // the parent we already know if we found one, and looping down the tree. - // We're contributing to the cache at the same time. - - // `index` is undefined if no parent was found in the cache. In that case we - // start from the start, and use `-1` which is the prefix we use to indicate - // the root node. - if (index === undefined) { - // assert(i === 0); - index = -1; - } - - while (i < callNodePath.length) { - // Resolving the index for subpath `callNodePath.slice(0, i+1)` given we - // know the index for the subpath `callNodePath.slice(0, i)` (its parent). - const func = callNodePath[i]; - const nextNodeIndex = getCallNodeIndexFromParentAndFunc( - index, - func, - callNodeTable - ); - - // We couldn't find this path into the call node table. This shouldn't - // normally happen. - if (nextNodeIndex === null) { - return null; - } - - // Contributing to the shared cache - const hash = sliceHashes.pop(); - cache.set(hash, nextNodeIndex); - - index = nextNodeIndex; - i++; - } - - return index < 0 ? null : index; -} - -// Returns the CallNodeIndex that matches the function `func` and whose parent's -// CallNodeIndex is `parent`. -export function getCallNodeIndexFromParentAndFunc( - parent: IndexIntoCallNodeTable | -1, - func: IndexIntoFuncTable, - callNodeTable: CallNodeTable -): IndexIntoCallNodeTable | null { - if (parent === -1) { - if (callNodeTable.length === 0) { - return null; - } - } else if (callNodeTable.subtreeRangeEnd[parent] === parent + 1) { - // parent has no children. - return null; - } - // Node children always come after their parents in the call node table, - // that's why we start looping at `parent + 1`. - // Note that because the root parent is `-1`, we correctly start at `0` when - // we look for a root. - const firstChild = parent + 1; - for ( - let callNodeIndex = firstChild; - callNodeIndex !== -1; - callNodeIndex = callNodeTable.nextSibling[callNodeIndex] - ) { - if (callNodeTable.func[callNodeIndex] === func) { - return callNodeIndex; - } - } - - return null; -} - -// This function returns a CallNodeIndex from a CallNodePath, using the -// specified `callNodeTable`. -export function getCallNodeIndexFromPath( - callNodePath: CallNodePath, - callNodeTable: CallNodeTable -): IndexIntoCallNodeTable | null { - const [result] = getCallNodeIndicesFromPaths([callNodePath], callNodeTable); - return result; -} - -// This function returns a CallNodePath from a CallNodeIndex. -export function getCallNodePathFromIndex( - callNodeIndex: IndexIntoCallNodeTable | null, - callNodeTable: CallNodeTable -): CallNodePath { - if (callNodeIndex === null || callNodeIndex === -1) { - return []; - } - - const callNodePath = []; - let fs = callNodeIndex; - while (fs !== -1) { - callNodePath.push(callNodeTable.func[fs]); - fs = callNodeTable.prefix[fs]; - } - callNodePath.reverse(); - return callNodePath; -} - /** * This function converts a stack information into a call node and * category path structure. @@ -2145,7 +2011,8 @@ export function computeCallNodeMaxDepthPlusOne( // computed for the filtered thread, but a samples-like table can use the preview // filtered thread, which involves a subset of the total call nodes. let maxDepth = -1; - const { callNodeTable, stackIndexToCallNodeIndex } = callNodeInfo; + const callNodeTable = callNodeInfo.getCallNodeTable(); + const stackIndexToCallNodeIndex = callNodeInfo.getStackIndexToCallNodeIndex(); for (let sampleIndex = 0; sampleIndex < samples.length; sampleIndex++) { const stackIndex = samples.stack[sampleIndex]; if (stackIndex === null) { @@ -3659,7 +3526,7 @@ export function getNativeSymbolsForCallNode( stackTable: StackTable, frameTable: FrameTable ): IndexIntoNativeSymbolTable[] { - if (callNodeInfo.isInverted) { + if (callNodeInfo.isInverted()) { return getNativeSymbolsForCallNodeInverted( callNodeIndex, callNodeInfo, @@ -3677,10 +3544,11 @@ export function getNativeSymbolsForCallNode( export function getNativeSymbolsForCallNodeNonInverted( callNodeIndex: IndexIntoCallNodeTable, - { stackIndexToCallNodeIndex }: CallNodeInfo, + callNodeInfo: CallNodeInfo, stackTable: StackTable, frameTable: FrameTable ): IndexIntoNativeSymbolTable[] { + const stackIndexToCallNodeIndex = callNodeInfo.getStackIndexToCallNodeIndex(); const set = new Set(); for (let stackIndex = 0; stackIndex < stackTable.length; stackIndex++) { if (stackIndexToCallNodeIndex[stackIndex] === callNodeIndex) { @@ -3700,11 +3568,11 @@ export function getNativeSymbolsForCallNodeInverted( stackTable: StackTable, frameTable: FrameTable ): IndexIntoNativeSymbolTable[] { - const invertedCallNodeTable = callNodeInfo.callNodeTable; + const invertedCallNodeTable = callNodeInfo.getCallNodeTable(); const depth = invertedCallNodeTable.depth[callNodeIndex]; const endIndex = invertedCallNodeTable.subtreeRangeEnd[callNodeIndex]; const stackTablePrefixCol = stackTable.prefix; - const { stackIndexToCallNodeIndex } = callNodeInfo; + const stackIndexToCallNodeIndex = callNodeInfo.getStackIndexToCallNodeIndex(); const set = new Set(); for (let stackIndex = 0; stackIndex < stackTable.length; stackIndex++) { const stackForNode = getMatchingAncestorStackForInvertedCallNode( @@ -3775,7 +3643,8 @@ export function getBottomBoxInfoForCallNode( nativeSymbols, } = thread; - const funcIndex = callNodeInfo.callNodeTable.func[callNodeIndex]; + const callNodeTable = callNodeInfo.getCallNodeTable(); + const funcIndex = callNodeTable.func[callNodeIndex]; const fileName = funcTable.fileName[funcIndex]; const sourceFile = fileName !== null ? stringTable.getString(fileName) : null; const resource = funcTable.resource[funcIndex]; diff --git a/src/profile-logic/stack-timing.js b/src/profile-logic/stack-timing.js index 37acb201cf..fc8c728a69 100644 --- a/src/profile-logic/stack-timing.js +++ b/src/profile-logic/stack-timing.js @@ -66,7 +66,7 @@ export function getStackTimingByDepth( maxDepthPlusOne: number, interval: Milliseconds ): StackTimingByDepth { - const { callNodeTable } = callNodeInfo; + const callNodeTable = callNodeInfo.getCallNodeTable(); const { prefix: callNodeTablePrefixColumn, subtreeRangeEnd: callNodeTableSubtreeRangeEndColumn, diff --git a/src/profile-logic/transforms.js b/src/profile-logic/transforms.js index 5f0ec3d565..dc95c8cd2a 100644 --- a/src/profile-logic/transforms.js +++ b/src/profile-logic/transforms.js @@ -12,7 +12,6 @@ import { updateThreadStacks, updateThreadStacksByGeneratingNewStackColumns, getMapStackUpdater, - getCallNodeIndexFromParentAndFunc, } from './profile-data'; import { timeCode } from '../utils/time-code'; import { assertExhaustiveCheck, convertToTransformType } from '../utils/flow'; @@ -32,6 +31,7 @@ import type { CallNodePath, CallNodeAndCategoryPath, CallNodeTable, + CallNodeInfo, StackType, ImplementationFilter, Transform, @@ -493,7 +493,7 @@ export function applyTransformToCallNodePath( callNodePath: CallNodePath, transform: Transform, transformedThread: Thread, - callNodeTable: CallNodeTable + callNodeInfo: CallNodeInfo ): CallNodePath { switch (transform.type) { case 'focus-subtree': @@ -507,7 +507,7 @@ export function applyTransformToCallNodePath( return _removeOtherCategoryFunctionsInNodePathWithFunction( transform.category, callNodePath, - callNodeTable + callNodeInfo ); case 'merge-call-node': return _mergeNodeInCallNodePath(transform.callNodePath, callNodePath); @@ -596,16 +596,17 @@ function _dropFunctionInCallNodePath( function _removeOtherCategoryFunctionsInNodePathWithFunction( category: IndexIntoCategoryList, callNodePath: CallNodePath, - callNodeTable: CallNodeTable + callNodeInfo: CallNodeInfo ): CallNodePath { + const callNodeTable = callNodeInfo.getCallNodeTable(); + const newCallNodePath = []; let prefix = -1; for (const funcIndex of callNodePath) { - const callNodeIndex = getCallNodeIndexFromParentAndFunc( + const callNodeIndex = callNodeInfo.getCallNodeIndexFromParentAndFunc( prefix, - funcIndex, - callNodeTable + funcIndex ); if (callNodeIndex === null) { throw new Error( diff --git a/src/reducers/profile-view.js b/src/reducers/profile-view.js index 139774e456..db968f64c8 100644 --- a/src/reducers/profile-view.js +++ b/src/reducers/profile-view.js @@ -346,8 +346,7 @@ const viewOptionsPerThread: Reducer = ( }); } case 'ADD_TRANSFORM_TO_STACK': { - const { threadsKey, transform, transformedThread, callNodeTable } = - action; + const { threadsKey, transform, transformedThread, callNodeInfo } = action; const getFilteredPathSet = function (pathSet: PathSet): PathSet { return new PathSet( @@ -357,7 +356,7 @@ const viewOptionsPerThread: Reducer = ( path, transform, transformedThread, - callNodeTable + callNodeInfo ) ) .filter((path) => path.length > 0) @@ -369,7 +368,7 @@ const viewOptionsPerThread: Reducer = ( path, transform, transformedThread, - callNodeTable + callNodeInfo ); }; diff --git a/src/selectors/per-thread/index.js b/src/selectors/per-thread/index.js index dae455e7e8..fa734d9565 100644 --- a/src/selectors/per-thread/index.js +++ b/src/selectors/per-thread/index.js @@ -289,8 +289,8 @@ export const selectedNodeSelectors: NodeSelectors = (() => { if (sourceViewFile === null || selectedCallNodeIndex === null) { return null; } - const selectedFunc = - callNodeInfo.callNodeTable.func[selectedCallNodeIndex]; + const callNodeTable = callNodeInfo.getCallNodeTable(); + const selectedFunc = callNodeTable.func[selectedCallNodeIndex]; const selectedFuncFile = funcTable.fileName[selectedFunc]; if ( selectedFuncFile === null || diff --git a/src/selectors/per-thread/stack-sample.js b/src/selectors/per-thread/stack-sample.js index 6f10bbd650..0bac1067a5 100644 --- a/src/selectors/per-thread/stack-sample.js +++ b/src/selectors/per-thread/stack-sample.js @@ -198,10 +198,7 @@ export function getStackAndSampleSelectorsPerThread( getCallNodeInfo, getSelectedCallNodePath, (callNodeInfo, callNodePath) => { - return ProfileData.getCallNodeIndexFromPath( - callNodePath, - callNodeInfo.callNodeTable - ); + return callNodeInfo.getCallNodeIndexFromPath(callNodePath); } ); @@ -219,10 +216,9 @@ export function getStackAndSampleSelectorsPerThread( > = createSelector( getCallNodeInfo, getExpandedCallNodePaths, - ({ callNodeTable }, callNodePaths) => - ProfileData.getCallNodeIndicesFromPaths( - Array.from(callNodePaths), - callNodeTable + (callNodeInfo, callNodePaths) => + Array.from(callNodePaths).map((path) => + callNodeInfo.getCallNodeIndexFromPath(path) ) ); @@ -230,8 +226,8 @@ export function getStackAndSampleSelectorsPerThread( Array, > = createSelector( (state) => threadSelectors.getFilteredThread(state).samples.stack, - getCallNodeInfo, - (filteredThreadSampleStacks, { stackIndexToCallNodeIndex }) => + (state) => getCallNodeInfo(state).getStackIndexToCallNodeIndex(), + (filteredThreadSampleStacks, stackIndexToCallNodeIndex) => ProfileData.getSampleIndexToCallNodeIndex( filteredThreadSampleStacks, stackIndexToCallNodeIndex @@ -242,8 +238,8 @@ export function getStackAndSampleSelectorsPerThread( Array, > = createSelector( (state) => threadSelectors.getTabFilteredThread(state).samples.stack, - getCallNodeInfo, - (tabFilteredThreadSampleStacks, { stackIndexToCallNodeIndex }) => + (state) => getCallNodeInfo(state).getStackIndexToCallNodeIndex(), + (tabFilteredThreadSampleStacks, stackIndexToCallNodeIndex) => ProfileData.getSampleIndexToCallNodeIndex( tabFilteredThreadSampleStacks, stackIndexToCallNodeIndex @@ -260,11 +256,11 @@ export function getStackAndSampleSelectorsPerThread( ( sampleIndexToCallNodeIndex, activeTabFilteredCallNodeIndex, - { callNodeTable }, + callNodeInfo, selectedCallNode ) => { return ProfileData.getSamplesSelectedStates( - callNodeTable, + callNodeInfo, sampleIndexToCallNodeIndex, activeTabFilteredCallNodeIndex, selectedCallNode @@ -313,7 +309,7 @@ export function getStackAndSampleSelectorsPerThread( const sampleIndexToCallNodeIndex = ProfileData.getSampleIndexToCallNodeIndex( samples.stack, - callNodeInfo.stackIndexToCallNodeIndex + callNodeInfo.getStackIndexToCallNodeIndex() ); return CallTree.computeCallTreeTimings( samples, @@ -365,7 +361,7 @@ export function getStackAndSampleSelectorsPerThread( ); const getFlameGraphRows: Selector = createSelector( - (state) => getCallNodeInfo(state).callNodeTable, + (state) => getCallNodeInfo(state).getCallNodeTable(), (state) => threadSelectors.getFilteredThread(state).funcTable, (state) => threadSelectors.getFilteredThread(state).stringTable, FlameGraph.computeFlameGraphRows @@ -374,7 +370,7 @@ export function getStackAndSampleSelectorsPerThread( const getFlameGraphTiming: Selector = createSelector( getFlameGraphRows, - (state) => getCallNodeInfo(state).callNodeTable, + (state) => getCallNodeInfo(state).getCallNodeTable(), getCallTreeTimings, FlameGraph.getFlameGraphTiming ); @@ -383,14 +379,13 @@ export function getStackAndSampleSelectorsPerThread( createSelector( getRightClickedCallNodeInfo, getCallNodeInfo, - (rightClickedCallNodeInfo, { callNodeTable }) => { + (rightClickedCallNodeInfo, callNodeInfo) => { if ( rightClickedCallNodeInfo !== null && threadsKey === rightClickedCallNodeInfo.threadsKey ) { - return ProfileData.getCallNodeIndexFromPath( - rightClickedCallNodeInfo.callNodePath, - callNodeTable + return callNodeInfo.getCallNodeIndexFromPath( + rightClickedCallNodeInfo.callNodePath ); } diff --git a/src/test/fixtures/utils.js b/src/test/fixtures/utils.js index 9a3367ce6d..8b329e7ad4 100644 --- a/src/test/fixtures/utils.js +++ b/src/test/fixtures/utils.js @@ -131,7 +131,7 @@ export function callTreeFromProfile( thread.samples, getSampleIndexToCallNodeIndex( thread.samples.stack, - callNodeInfo.stackIndexToCallNodeIndex + callNodeInfo.getStackIndexToCallNodeIndex() ), callNodeInfo, false diff --git a/src/test/store/__snapshots__/profile-view.test.js.snap b/src/test/store/__snapshots__/profile-view.test.js.snap index 04a4821036..cfd72bf73c 100644 --- a/src/test/store/__snapshots__/profile-view.test.js.snap +++ b/src/test/store/__snapshots__/profile-view.test.js.snap @@ -2182,8 +2182,9 @@ Object { `; exports[`snapshots of selectors/profile matches the last stored run of selectedThreadSelector.getCallNodeInfo 1`] = ` -Object { - "callNodeTable": Object { +CallNodeInfoImpl { + "_cache": Map {}, + "_callNodeTable": Object { "category": Int32Array [ 0, 0, @@ -2286,8 +2287,8 @@ Object { 9, ], }, - "isInverted": false, - "stackIndexToCallNodeIndex": Int32Array [ + "_isInverted": false, + "_stackIndexToCallNodeIndex": Int32Array [ 0, 1, 2, @@ -2314,8 +2315,9 @@ CallTree { 0, 0, ], - "_callNodeInfo": Object { - "callNodeTable": Object { + "_callNodeInfo": CallNodeInfoImpl { + "_cache": Map {}, + "_callNodeTable": Object { "category": Int32Array [ 0, 0, @@ -2418,8 +2420,8 @@ CallTree { 9, ], }, - "isInverted": false, - "stackIndexToCallNodeIndex": Int32Array [ + "_isInverted": false, + "_stackIndexToCallNodeIndex": Int32Array [ 0, 1, 2, diff --git a/src/test/store/actions.test.js b/src/test/store/actions.test.js index 4527f8d77c..cddcf82e47 100644 --- a/src/test/store/actions.test.js +++ b/src/test/store/actions.test.js @@ -146,9 +146,10 @@ describe('selectors/getFlameGraphTiming', function () { * "FunctionName1 (StartTime:EndTime) | FunctionName2 (StartTime:EndTime)" */ function getHumanReadableFlameGraphRanges(store, funcNames) { - const { callNodeTable } = selectedThreadSelectors.getCallNodeInfo( + const callNodeInfo = selectedThreadSelectors.getCallNodeInfo( store.getState() ); + const callNodeTable = callNodeInfo.getCallNodeTable(); const flameGraphTiming = selectedThreadSelectors.getFlameGraphTiming( store.getState() ); @@ -176,9 +177,10 @@ describe('selectors/getFlameGraphTiming', function () { * "FunctionName1 (SelfTimeRelative) | ..." */ function getHumanReadableFlameGraphTimings(store, funcNames) { - const { callNodeTable } = selectedThreadSelectors.getCallNodeInfo( + const callNodeInfo = selectedThreadSelectors.getCallNodeInfo( store.getState() ); + const callNodeTable = callNodeInfo.getCallNodeTable(); const flameGraphTiming = selectedThreadSelectors.getFlameGraphTiming( store.getState() ); diff --git a/src/test/store/bottom-box.test.js b/src/test/store/bottom-box.test.js index 90439b28ac..cb847c5b91 100644 --- a/src/test/store/bottom-box.test.js +++ b/src/test/store/bottom-box.test.js @@ -10,10 +10,7 @@ import { selectedNodeSelectors, } from '../../selectors/per-thread'; import { emptyAddressTimings } from '../../profile-logic/address-timings'; -import { - getBottomBoxInfoForCallNode, - getCallNodeIndexFromPath, -} from '../../profile-logic/profile-data'; +import { getBottomBoxInfoForCallNode } from '../../profile-logic/profile-data'; import { changeSelectedCallNode, updateBottomBoxContentsAndMaybeOpen, @@ -83,7 +80,7 @@ describe('bottom box', function () { // Simulate double-clicking the call node at [A, B, C, D]. const abcd = ensureExists( - getCallNodeIndexFromPath([A, B, C, D], callNodeInfo.callNodeTable) + callNodeInfo.getCallNodeIndexFromPath([A, B, C, D]) ); const nativeSymbolInfoD = { libIndex: 0, @@ -171,9 +168,7 @@ describe('bottom box', function () { // [selected tab: calltree] [calltree: bottombox open] [flame-graph: bottombox open] [assembly view closed] // Double-click a call node which doesn't have source file information. - const aef = ensureExists( - getCallNodeIndexFromPath([A, E, F], callNodeInfo.callNodeTable) - ); + const aef = ensureExists(callNodeInfo.getCallNodeIndexFromPath([A, E, F])); const nativeSymbolInfoF = { libIndex: 1, address: 0x12, @@ -216,9 +211,7 @@ describe('bottom box', function () { // Simulate double-clicking the call node at [A, B, C]. // This call node has been inlined into B and into A. - const abc = ensureExists( - getCallNodeIndexFromPath([A, B, C], callNodeInfo.callNodeTable) - ); + const abc = ensureExists(callNodeInfo.getCallNodeIndexFromPath([A, B, C])); const nativeSymbolInfoA = { libIndex: 0, address: 0x20, @@ -268,9 +261,7 @@ describe('bottom box', function () { // box with that info. dispatch(changeSelectedCallNode(threadsKey, [A, B, C, D])); const bottomBoxInfoABC = getBottomBoxInfoForCallNode( - ensureExists( - getCallNodeIndexFromPath([A, B, C, D], callNodeInfo.callNodeTable) - ), + ensureExists(callNodeInfo.getCallNodeIndexFromPath([A, B, C, D])), callNodeInfo, thread ); diff --git a/src/test/store/profile-view.test.js b/src/test/store/profile-view.test.js index 03598c9b53..f2b6bd6e76 100644 --- a/src/test/store/profile-view.test.js +++ b/src/test/store/profile-view.test.js @@ -48,7 +48,6 @@ import { } from '../../selectors/per-thread'; import { ensureExists } from '../../utils/flow'; import { - getCallNodeIndexFromPath, processCounter, type BreakdownByCategory, } from '../../profile-logic/profile-data'; @@ -3286,8 +3285,7 @@ describe('traced timing', function () { ); } - const { callNodeTable } = - selectedThreadSelectors.getCallNodeInfo(getState()); + const callNodeInfo = selectedThreadSelectors.getCallNodeInfo(getState()); const { running, self } = ensureExists( selectedThreadSelectors.getTracedTiming(getState()), @@ -3297,7 +3295,7 @@ describe('traced timing', function () { return { funcNames: funcNamesDictPerThread[0], getCallNode: (...callNodePath) => - ensureExists(getCallNodeIndexFromPath(callNodePath, callNodeTable)), + ensureExists(callNodeInfo.getCallNodeIndexFromPath(callNodePath)), running, self, profile, diff --git a/src/test/unit/address-timings.test.js b/src/test/unit/address-timings.test.js index adf2c69476..101da31661 100644 --- a/src/test/unit/address-timings.test.js +++ b/src/test/unit/address-timings.test.js @@ -13,7 +13,6 @@ import { import { getCallNodeInfo, getInvertedCallNodeInfo, - getCallNodeIndexFromPath, } from '../../profile-logic/profile-data'; import { ensureExists } from 'firefox-profiler/utils/flow'; import type { @@ -163,7 +162,7 @@ describe('getAddressTimings for getStackAddressInfoForCallNode', function () { ? getInvertedCallNodeInfo(thread, defaultCat) : getCallNodeInfo(stackTable, frameTable, funcTable, defaultCat); const callNodeIndex = ensureExists( - getCallNodeIndexFromPath(callNodePath, callNodeInfo.callNodeTable), + callNodeInfo.getCallNodeIndexFromPath(callNodePath), 'invalid call node path' ); const stackLineInfo = getStackAddressInfoForCallNode( diff --git a/src/test/unit/line-timings.test.js b/src/test/unit/line-timings.test.js index be8eda4659..63ef358c6c 100644 --- a/src/test/unit/line-timings.test.js +++ b/src/test/unit/line-timings.test.js @@ -13,7 +13,6 @@ import { import { getCallNodeInfo, getInvertedCallNodeInfo, - getCallNodeIndexFromPath, } from '../../profile-logic/profile-data'; import { ensureExists } from 'firefox-profiler/utils/flow'; import type { @@ -126,7 +125,7 @@ describe('getLineTimings for getStackLineInfoForCallNode', function () { ? getInvertedCallNodeInfo(thread, defaultCat) : getCallNodeInfo(stackTable, frameTable, funcTable, defaultCat); const callNodeIndex = ensureExists( - getCallNodeIndexFromPath(callNodePath, callNodeInfo.callNodeTable), + callNodeInfo.getCallNodeIndexFromPath(callNodePath), 'invalid call node path' ); const stackLineInfo = getStackLineInfoForCallNode( diff --git a/src/test/unit/profile-data.test.js b/src/test/unit/profile-data.test.js index b28d1c83cb..32a7f67cd9 100644 --- a/src/test/unit/profile-data.test.js +++ b/src/test/unit/profile-data.test.js @@ -15,11 +15,9 @@ import { getCallNodeInfo, getInvertedCallNodeInfo, filterThreadByImplementation, - getCallNodePathFromIndex, getSampleIndexClosestToStartTime, convertStackToCallNodeAndCategoryPath, getSampleIndexToCallNodeIndex, - getCallNodeIndexFromPath, getTreeOrderComparator, getSamplesSelectedStates, extractProfileFilterPageData, @@ -446,12 +444,13 @@ describe('profile-data', function () { 'Expected to find categories' ).findIndex((c) => c.name === 'Other'); const thread = profile.threads[0]; - const { callNodeTable } = getCallNodeInfo( + const callNodeInfo = getCallNodeInfo( thread.stackTable, thread.frameTable, thread.funcTable, defaultCategory ); + const callNodeTable = callNodeInfo.getCallNodeTable(); it('should create one callNode per original stack', function () { // After nudgeReturnAddresses, the stack table now has 8 entries. @@ -500,12 +499,15 @@ describe('profile-data', function () { meta.categories, 'Expected to find categories' ).findIndex((c) => c.name === 'Other'); - const { callNodeTable, stackIndexToCallNodeIndex } = getCallNodeInfo( + const callNodeInfo = getCallNodeInfo( thread.stackTable, thread.frameTable, thread.funcTable, defaultCategory ); + const callNodeTable = callNodeInfo.getCallNodeTable(); + const stackIndexToCallNodeIndex = + callNodeInfo.getStackIndexToCallNodeIndex(); const stack0 = thread.samples.stack[0]; const stack1 = thread.samples.stack[1]; if (stack0 === null || stack1 === null) { @@ -513,13 +515,11 @@ describe('profile-data', function () { } const originalStackListA = _getStackList(thread, stack0); const originalStackListB = _getStackList(thread, stack1); - const mergedFuncListA = getCallNodePathFromIndex( - stackIndexToCallNodeIndex[stack0], - callNodeTable + const mergedFuncListA = callNodeInfo.getCallNodePathFromIndex( + stackIndexToCallNodeIndex[stack0] ); - const mergedFuncListB = getCallNodePathFromIndex( - stackIndexToCallNodeIndex[stack1], - callNodeTable + const mergedFuncListB = callNodeInfo.getCallNodePathFromIndex( + stackIndexToCallNodeIndex[stack1] ); it('starts with a fully unduplicated set stack frames', function () { @@ -785,19 +785,19 @@ describe('funcHasDirectRecursiveCall and funcHasRecursiveCall', function () { profile.meta.categories, 'Expected to find categories' ).findIndex((c) => c.name === 'Other'); - const { callNodeTable } = getCallNodeInfo( + const callNodeTable = getCallNodeInfo( thread.stackTable, thread.frameTable, thread.funcTable, defaultCategory - ); + ).getCallNodeTable(); const jsOnlyThread = filterThreadByImplementation(thread, 'js'); - const { callNodeTable: jsOnlyCallNodeTable } = getCallNodeInfo( + const jsOnlyCallNodeTable = getCallNodeInfo( jsOnlyThread.stackTable, jsOnlyThread.frameTable, jsOnlyThread.funcTable, defaultCategory - ); + ).getCallNodeTable(); return { callNodeTable, jsOnlyCallNodeTable, funcNames }; } @@ -868,26 +868,27 @@ describe('getSamplesSelectedStates', function () { C E F G `); const thread = profile.threads[0]; - const { callNodeTable, stackIndexToCallNodeIndex } = getCallNodeInfo( + const callNodeInfo = getCallNodeInfo( thread.stackTable, thread.frameTable, thread.funcTable, 0 ); + const stackIndexToCallNodeIndex = callNodeInfo.getStackIndexToCallNodeIndex(); const sampleCallNodes = getSampleIndexToCallNodeIndex( thread.samples.stack, stackIndexToCallNodeIndex ); - const A_B = getCallNodeIndexFromPath([A, B], callNodeTable); - const A_B_F = getCallNodeIndexFromPath([A, B, F], callNodeTable); - const A_D = getCallNodeIndexFromPath([A, D], callNodeTable); - const A_D_E = getCallNodeIndexFromPath([A, D, E], callNodeTable); + const A_B = callNodeInfo.getCallNodeIndexFromPath([A, B]); + const A_B_F = callNodeInfo.getCallNodeIndexFromPath([A, B, F]); + const A_D = callNodeInfo.getCallNodeIndexFromPath([A, D]); + const A_D_E = callNodeInfo.getCallNodeIndexFromPath([A, D, E]); it('determines the selection status of all the samples', function () { expect( getSamplesSelectedStates( - callNodeTable, + callNodeInfo, sampleCallNodes, sampleCallNodes, A_B @@ -901,7 +902,7 @@ describe('getSamplesSelectedStates', function () { ]); expect( getSamplesSelectedStates( - callNodeTable, + callNodeInfo, sampleCallNodes, sampleCallNodes, A_D @@ -915,7 +916,7 @@ describe('getSamplesSelectedStates', function () { ]); expect( getSamplesSelectedStates( - callNodeTable, + callNodeInfo, sampleCallNodes, sampleCallNodes, A_B_F @@ -929,7 +930,7 @@ describe('getSamplesSelectedStates', function () { ]); expect( getSamplesSelectedStates( - callNodeTable, + callNodeInfo, sampleCallNodes, sampleCallNodes, A_D_E @@ -1153,15 +1154,9 @@ describe('getNativeSymbolsForCallNode', function () { thread.funcTable, defaultCategory ); - const ab = getCallNodeIndexFromPath( - [funA, funB], - callNodeInfo.callNodeTable - ); + const ab = callNodeInfo.getCallNodeIndexFromPath([funA, funB]); expect(ab).not.toBeNull(); - const abc = getCallNodeIndexFromPath( - [funA, funB, funC], - callNodeInfo.callNodeTable - ); + const abc = callNodeInfo.getCallNodeIndexFromPath([funA, funB, funC]); expect(abc).not.toBeNull(); // Both the call path [funA, funB] and the call path [funA, funB, funC] end @@ -1201,7 +1196,7 @@ describe('getNativeSymbolsForCallNode', function () { ); const defaultCategory = categories.findIndex((c) => c.name === 'Other'); const callNodeInfo = getInvertedCallNodeInfo(thread, defaultCategory); - const c = getCallNodeIndexFromPath([funC], callNodeInfo.callNodeTable); + const c = callNodeInfo.getCallNodeIndexFromPath([funC]); expect(c).not.toBeNull(); // The call node for funC in the inverted thread has one sample where funC diff --git a/src/test/unit/profile-tree.test.js b/src/test/unit/profile-tree.test.js index 65041d4456..2c14121fa1 100644 --- a/src/test/unit/profile-tree.test.js +++ b/src/test/unit/profile-tree.test.js @@ -14,7 +14,6 @@ import { computeFlameGraphRows } from '../../profile-logic/flame-graph'; import { getCallNodeInfo, getInvertedCallNodeInfo, - getCallNodeIndexFromPath, getOriginAnnotationForFunc, filterThreadSamplesToRange, getSampleIndexToCallNodeIndex, @@ -75,7 +74,7 @@ describe('unfiltered call tree', function () { thread.samples, getSampleIndexToCallNodeIndex( thread.samples.stack, - callNodeInfo.stackIndexToCallNodeIndex + callNodeInfo.getStackIndexToCallNodeIndex() ), callNodeInfo, false @@ -110,26 +109,26 @@ describe('unfiltered call tree', function () { profile.meta.categories, 'Expected to find categories' ).findIndex((c) => c.name === 'Other'); - const { callNodeTable } = getCallNodeInfo( + const callNodeInfo = getCallNodeInfo( thread.stackTable, thread.frameTable, thread.funcTable, defaultCategory ); - const cnZ = getCallNodeIndexFromPath([Z], callNodeTable); - const cnZX = getCallNodeIndexFromPath([Z, X], callNodeTable); - const cnZXY = getCallNodeIndexFromPath([Z, X, Y], callNodeTable); - const cnZXW = getCallNodeIndexFromPath([Z, X, W], callNodeTable); - const cnG = getCallNodeIndexFromPath([G], callNodeTable); - const cnGH = getCallNodeIndexFromPath([G, H], callNodeTable); - const cnGHI = getCallNodeIndexFromPath([G, H, I], callNodeTable); - const cnGHJ = getCallNodeIndexFromPath([G, H, J], callNodeTable); - const cnK = getCallNodeIndexFromPath([K], callNodeTable); - const cnKM = getCallNodeIndexFromPath([K, M], callNodeTable); - const cnKN = getCallNodeIndexFromPath([K, N], callNodeTable); + const cnZ = callNodeInfo.getCallNodeIndexFromPath([Z]); + const cnZX = callNodeInfo.getCallNodeIndexFromPath([Z, X]); + const cnZXY = callNodeInfo.getCallNodeIndexFromPath([Z, X, Y]); + const cnZXW = callNodeInfo.getCallNodeIndexFromPath([Z, X, W]); + const cnG = callNodeInfo.getCallNodeIndexFromPath([G]); + const cnGH = callNodeInfo.getCallNodeIndexFromPath([G, H]); + const cnGHI = callNodeInfo.getCallNodeIndexFromPath([G, H, I]); + const cnGHJ = callNodeInfo.getCallNodeIndexFromPath([G, H, J]); + const cnK = callNodeInfo.getCallNodeIndexFromPath([K]); + const cnKM = callNodeInfo.getCallNodeIndexFromPath([K, M]); + const cnKN = callNodeInfo.getCallNodeIndexFromPath([K, N]); const rows = computeFlameGraphRows( - callNodeTable, + callNodeInfo.getCallNodeTable(), thread.funcTable, thread.stringTable ); @@ -373,7 +372,7 @@ describe('unfiltered call tree', function () { profile.meta.categories, 'Expected to find categories' ).findIndex((c) => c.name === 'Other'); - const { callNodeTable } = getCallNodeInfo( + const callNodeInfo = getCallNodeInfo( thread.stackTable, thread.frameTable, thread.funcTable, @@ -383,9 +382,7 @@ describe('unfiltered call tree', function () { // Helper to make the assertions a little less verbose. function checkStack(callNodePath, index, name) { it(`finds stack that ends in ${name}`, function () { - expect(getCallNodeIndexFromPath(callNodePath, callNodeTable)).toBe( - index - ); + expect(callNodeInfo.getCallNodeIndexFromPath(callNodePath)).toBe(index); }); } @@ -402,9 +399,9 @@ describe('unfiltered call tree', function () { checkStack([A, B, H, I], I, 'I'); it(`doesn't find a non-existent stack`, function () { - expect( - getCallNodeIndexFromPath([A, B, C, D, E, F, G], callNodeTable) - ).toBe(null); + expect(callNodeInfo.getCallNodeIndexFromPath([A, B, C, D, E, F, G])).toBe( + null + ); }); }); }); @@ -440,7 +437,7 @@ describe('inverted call tree', function () { thread.samples, getSampleIndexToCallNodeIndex( thread.samples.stack, - callNodeInfo.stackIndexToCallNodeIndex + callNodeInfo.getStackIndexToCallNodeIndex() ), callNodeInfo, false @@ -478,7 +475,7 @@ describe('inverted call tree', function () { thread.samples, getSampleIndexToCallNodeIndex( thread.samples.stack, - invertedCallNodeInfo.stackIndexToCallNodeIndex + invertedCallNodeInfo.getStackIndexToCallNodeIndex() ), invertedCallNodeInfo, true @@ -628,7 +625,7 @@ describe('diffing trees', function () { thread.samples, getSampleIndexToCallNodeIndex( thread.samples.stack, - callNodeInfo.stackIndexToCallNodeIndex + callNodeInfo.getStackIndexToCallNodeIndex() ), callNodeInfo, false diff --git a/src/types/actions.js b/src/types/actions.js index 7dbe06b600..b9c81364d9 100644 --- a/src/types/actions.js +++ b/src/types/actions.js @@ -16,7 +16,7 @@ import type { } from './profile'; import type { CallNodePath, - CallNodeTable, + CallNodeInfo, GlobalTrack, LocalTrack, TrackIndex, @@ -478,7 +478,7 @@ type UrlStateAction = +threadsKey: ThreadsKey, +transform: Transform, +transformedThread: Thread, - +callNodeTable: CallNodeTable, + +callNodeInfo: CallNodeInfo, |} | {| +type: 'POP_TRANSFORMS_FROM_STACK', diff --git a/src/types/profile-derived.js b/src/types/profile-derived.js index 2be2336875..841af62a39 100644 --- a/src/types/profile-derived.js +++ b/src/types/profile-derived.js @@ -122,12 +122,20 @@ export type CallNodeTable = { }; /** - * Both the callNodeTable and a map that converts an IndexIntoStackTable - * into an IndexIntoCallNodeTable. + * Wraps the call node table and provides associated functionality. */ -export type CallNodeInfo = { - callNodeTable: CallNodeTable, - // IndexIntoStackTable -> IndexIntoCallNodeTable | -1 +export interface CallNodeInfo { + // If true, call node indexes describe nodes in the inverted call tree. + isInverted(): boolean; + + // Returns the call node table. If isInverted() is true, this is an inverted + // call node table, otherwise this is the non-inverted call node table. + getCallNodeTable(): CallNodeTable; + + // Returns a mapping from the stack table to the call node table. + // The Int32Array should be used as if it were a + // Map. + // // If this CallNodeInfo is for the non-inverted tree, this maps the stack index // to its corresponding call node index, and all entries are >= 0. // If this CallNodeInfo is for the inverted tree, this maps the non-inverted @@ -140,11 +148,27 @@ export type CallNodeInfo = { // of the A -> B -> C -> D stack and no sample / marker / allocation has // A -> B -> C as its stack, then there is no need to have a call node // C <- B <- A in the inverted call node table. - stackIndexToCallNodeIndex: Int32Array, - // Whether the call node table in this call node info describes the inverted - // call tree. - isInverted: boolean, -}; + getStackIndexToCallNodeIndex(): Int32Array; + + // Converts a call node index into a call node path. + getCallNodePathFromIndex( + callNodeIndex: IndexIntoCallNodeTable | null + ): CallNodePath; + + // Converts a call node path into a call node index. + getCallNodeIndexFromPath( + callNodePath: CallNodePath + ): IndexIntoCallNodeTable | null; + + // Returns the call node index that matches the function `func` and whose + // parent's index is `parent`. If `parent` is -1, this returns the index of + // the root node with function `func`. + // Returns null if the described call node doesn't exist. + getCallNodeIndexFromParentAndFunc( + parent: IndexIntoCallNodeTable | -1, + func: IndexIntoFuncTable + ): IndexIntoCallNodeTable | null; +} export type LineNumber = number; From a91f2694cb7ead2ac2b0463d6f68a0856cf30d25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9o=20Chevalier?= Date: Tue, 23 Jan 2024 17:52:30 +0000 Subject: [PATCH 23/55] Pontoon: Update French (fr) localization of Firefox Profiler MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Théo Chevalier --- locales/fr/app.ftl | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/locales/fr/app.ftl b/locales/fr/app.ftl index 836287ff3f..d964e7e876 100644 --- a/locales/fr/app.ftl +++ b/locales/fr/app.ftl @@ -759,6 +759,46 @@ TrackPower--tooltip-energy-carbon-used-in-preview-milliwatthour = { $value } mW TrackPower--tooltip-energy-carbon-used-in-preview-microwatthour = { $value } µWh ({ $carbonValue } mg eqCO₂) .label = Énergie consommée dans la sélection courante +## TrackBandwidth +## This is used to show how much data was transfered over time. +## For the strings in this group, the carbon dioxide equivalent is estimated +## from the amount of data transfered. +## The carbon dioxide equivalent represents the equivalent amount +## of CO₂ to achieve the same level of global warming potential. + +# This is used in the tooltip of the bandwidth track. +# Variables: +# $value (String) - the value for the data transfer speed. +# Will contain the unit (eg. B, KB, MB) +TrackBandwidthGraph--speed = { $value } par seconde + .label = Vitesse de transfert pour cet échantillon +# This is used in the tooltip of the bandwidth track. +# Variables: +# $value (String) - how many read or write operations were performed since the previous sample +TrackBandwidthGraph--read-write-operations-since-the-previous-sample = { $value } + .label = opérations de lecture/écriture depuis le précédent échantillon +# This is used in the tooltip of the bandwidth track. +# Variables: +# $value (String) - the total of transfered data until the hovered time. +# Will contain the unit (eg. B, KB, MB) +# $carbonValue (string) - the carbon dioxide equivalent (CO₂e) value in grams +TrackBandwidthGraph--cumulative-bandwidth-at-this-time = { $value } ({ $carbonValue } g eqCO₂) + .label = Données transférées jusqu’à présent +# This is used in the tooltip of the bandwidth track. +# Variables: +# $value (String) - the total of transfered data during the visible time range. +# Will contain the unit (eg. B, KB, MB) +# $carbonValue (string) - the carbon dioxide equivalent (CO₂e) value in grams +TrackBandwidthGraph--total-bandwidth-in-graph = { $value } ({ $carbonValue } g eqCO₂) + .label = Données transférées dans l’intervalle visible +# This is used in the tooltip of the bandwidth track when a range is selected. +# Variables: +# $value (String) - the total of transfered data during the selected time range. +# Will contain the unit (eg. B, KB, MB) +# $carbonValue (string) - the carbon dioxide equivalent (CO₂e) value in grams +TrackBandwidthGraph--total-bandwidth-in-range = { $value } ({ $carbonValue } g eqCO₂) + .label = Données transférées dans la sélection courante + ## TrackSearchField ## The component that is used for the search input in the track context menu. From 48ef450057c4f5406aef3bdd9b3f07f903d8ab2f Mon Sep 17 00:00:00 2001 From: Markus Stange Date: Fri, 19 Jan 2024 15:04:05 -0500 Subject: [PATCH 24/55] Remove unused rootCount field. This became unused in https://github.com/firefox-devtools/profiler/commit/e0edc96eb5592805810132ba71234bcde2ec8831 , part of #4807. --- src/profile-logic/call-tree.js | 19 +++---------------- .../__snapshots__/profile-view.test.js.snap | 1 - src/test/unit/profile-tree.test.js | 1 - 3 files changed, 3 insertions(+), 18 deletions(-) diff --git a/src/profile-logic/call-tree.js b/src/profile-logic/call-tree.js index 6ac2174eac..8d04a4c037 100644 --- a/src/profile-logic/call-tree.js +++ b/src/profile-logic/call-tree.js @@ -47,7 +47,6 @@ type CallNodeSummary = { export type CallTreeTimings = { callNodeHasChildren: Uint8Array, callNodeSummary: CallNodeSummary, - rootCount: number, rootTotalSummary: number, }; @@ -76,7 +75,6 @@ export class CallTree { _callNodeHasChildren: Uint8Array; // A table column matching the callNodeTable _thread: Thread; _rootTotalSummary: number; - _rootCount: number; _displayDataByIndex: Map; // _children is indexed by IndexIntoCallNodeTable. Since they are // integers, using an array directly is faster than going through a Map. @@ -91,7 +89,6 @@ export class CallTree { callNodeSummary: CallNodeSummary, callNodeHasChildren: Uint8Array, rootTotalSummary: number, - rootCount: number, isHighPrecision: boolean, weightType: WeightType ) { @@ -102,7 +99,6 @@ export class CallTree { this._callNodeHasChildren = callNodeHasChildren; this._thread = thread; this._rootTotalSummary = rootTotalSummary; - this._rootCount = rootCount; this._displayDataByIndex = new Map(); this._children = []; this._isHighPrecision = isHighPrecision; @@ -528,7 +524,6 @@ export function computeCallTreeTimings( const callNodeTotalSummary = new Float32Array(callNodeTable.length); const callNodeHasChildren = new Uint8Array(callNodeTable.length); let rootTotalSummary = 0; - let rootCount = 0; // Workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=1858310 const abs = Math.abs; @@ -551,9 +546,7 @@ export function computeCallTreeTimings( } const prefixCallNode = callNodeTable.prefix[callNodeIndex]; - if (prefixCallNode === -1) { - rootCount++; - } else { + if (prefixCallNode !== -1) { callNodeTotalSummary[prefixCallNode] += callNodeTotalSummary[callNodeIndex]; callNodeHasChildren[prefixCallNode] = 1; @@ -568,7 +561,6 @@ export function computeCallTreeTimings( }, callNodeHasChildren, rootTotalSummary, - rootCount, }; } @@ -583,12 +575,8 @@ export function getCallTree( weightType: WeightType ): CallTree { return timeCode('getCallTree', () => { - const { - callNodeSummary, - callNodeHasChildren, - rootTotalSummary, - rootCount, - } = callTreeTimings; + const { callNodeSummary, callNodeHasChildren, rootTotalSummary } = + callTreeTimings; return new CallTree( thread, @@ -597,7 +585,6 @@ export function getCallTree( callNodeSummary, callNodeHasChildren, rootTotalSummary, - rootCount, Boolean(thread.isJsTracer), weightType ); diff --git a/src/test/store/__snapshots__/profile-view.test.js.snap b/src/test/store/__snapshots__/profile-view.test.js.snap index cfd72bf73c..b112486454 100644 --- a/src/test/store/__snapshots__/profile-view.test.js.snap +++ b/src/test/store/__snapshots__/profile-view.test.js.snap @@ -2632,7 +2632,6 @@ CallTree { "_children": Array [], "_displayDataByIndex": Map {}, "_isHighPrecision": false, - "_rootCount": 1, "_rootTotalSummary": 2, "_thread": Object { "frameTable": Object { diff --git a/src/test/unit/profile-tree.test.js b/src/test/unit/profile-tree.test.js index 2c14121fa1..d0d6770716 100644 --- a/src/test/unit/profile-tree.test.js +++ b/src/test/unit/profile-tree.test.js @@ -80,7 +80,6 @@ describe('unfiltered call tree', function () { false ) ).toEqual({ - rootCount: 1, rootTotalSummary: 3, callNodeHasChildren: new Uint8Array([1, 1, 1, 1, 0, 1, 0, 1, 0]), callNodeSummary: { From 6f4c71ab5871c2dca0dad9560044c5e780b94cfc Mon Sep 17 00:00:00 2001 From: Markus Stange Date: Mon, 15 Jan 2024 18:52:54 -0500 Subject: [PATCH 25/55] Only pass the traced self/total for a single node to the CallTreeSidebar. With the fast inverted tree implementation, getting the running/total for an inverted call node will require a function call and not just a lookup in a big array. This is a step towards that world. --- src/components/sidebar/CallTreeSidebar.js | 19 +++--- src/selectors/per-thread/stack-sample.js | 16 ++++++ src/test/store/profile-view.test.js | 70 ++++++++--------------- src/types/profile-derived.js | 10 ++++ 4 files changed, 61 insertions(+), 54 deletions(-) diff --git a/src/components/sidebar/CallTreeSidebar.js b/src/components/sidebar/CallTreeSidebar.js index 56fa52d85b..f36eb9a310 100644 --- a/src/components/sidebar/CallTreeSidebar.js +++ b/src/components/sidebar/CallTreeSidebar.js @@ -30,7 +30,7 @@ import type { ThreadsKey, CategoryList, IndexIntoCallNodeTable, - TracedTiming, + SelfAndTotal, Milliseconds, WeightType, IndexIntoCategoryList, @@ -291,7 +291,7 @@ type StateProps = {| +timings: TimingsForPath, +categoryList: CategoryList, +weightType: WeightType, - +tracedTiming: TracedTiming | null, + +selectedNodeTracedSelfAndTotal: SelfAndTotal | null, |}; type Props = ConnectedProps<{||}, StateProps, {||}>; @@ -352,7 +352,7 @@ class CallTreeSidebarImpl extends React.PureComponent { timings, categoryList, weightType, - tracedTiming, + selectedNodeTracedSelfAndTotal, } = this.props; const { forPath: { selfTime, totalTime }, @@ -402,24 +402,24 @@ class CallTreeSidebarImpl extends React.PureComponent {

Call node details

- {tracedTiming ? ( + {selectedNodeTracedSelfAndTotal ? ( ) : null} - {tracedTiming ? ( + {selectedNodeTracedSelfAndTotal ? ( ({ timings: selectedNodeSelectors.getTimingsForSidebar(state), categoryList: getCategories(state), weightType: selectedThreadSelectors.getWeightTypeForCallTree(state), - tracedTiming: selectedThreadSelectors.getTracedTiming(state), + selectedNodeTracedSelfAndTotal: + selectedThreadSelectors.getTracedSelfAndTotalForSelectedCallNode(state), }), component: CallTreeSidebarImpl, }); diff --git a/src/selectors/per-thread/stack-sample.js b/src/selectors/per-thread/stack-sample.js index 0bac1067a5..7bd029ccf5 100644 --- a/src/selectors/per-thread/stack-sample.js +++ b/src/selectors/per-thread/stack-sample.js @@ -44,6 +44,7 @@ import type { $ReturnType, TracedTiming, ThreadsKey, + SelfAndTotal, } from 'firefox-profiler/types'; import type { ThreadSelectorsPerThread } from './thread'; @@ -350,6 +351,20 @@ export function getStackAndSampleSelectorsPerThread( CallTree.computeTracedTiming ); + const getTracedSelfAndTotalForSelectedCallNode: Selector = + createSelector( + getSelectedCallNodeIndex, + getTracedTiming, + (selectedCallNodeIndex, tracedTiming) => { + if (selectedCallNodeIndex === null || tracedTiming === null) { + return null; + } + const total = tracedTiming.running[selectedCallNodeIndex]; + const self = tracedTiming.self[selectedCallNodeIndex]; + return { total, self }; + } + ); + const getStackTimingByDepth: Selector = createSelector( threadSelectors.getFilteredSamplesForCallTree, @@ -412,6 +427,7 @@ export function getStackAndSampleSelectorsPerThread( getSourceViewLineTimings, getAssemblyViewAddressTimings, getTracedTiming, + getTracedSelfAndTotalForSelectedCallNode, getStackTimingByDepth, getFilteredCallNodeMaxDepthPlusOne, getPreviewFilteredCallNodeMaxDepthPlusOne, diff --git a/src/test/store/profile-view.test.js b/src/test/store/profile-view.test.js index f2b6bd6e76..7578e34ab8 100644 --- a/src/test/store/profile-view.test.js +++ b/src/test/store/profile-view.test.js @@ -3294,10 +3294,12 @@ describe('traced timing', function () { return { funcNames: funcNamesDictPerThread[0], - getCallNode: (...callNodePath) => - ensureExists(callNodeInfo.getCallNodeIndexFromPath(callNodePath)), - running, - self, + getSelfAndTotal: (...callNodePath) => { + const callNodeIndex = ensureExists( + callNodeInfo.getCallNodeIndexFromPath(callNodePath) + ); + return { self: self[callNodeIndex], total: running[callNodeIndex] }; + }, profile, }; } @@ -3305,9 +3307,7 @@ describe('traced timing', function () { it('computes traced timing', function () { const { funcNames: { A, B, C }, - getCallNode, - running, - self, + getSelfAndTotal, profile, } = setup( { inverted: false }, @@ -3318,24 +3318,18 @@ describe('traced timing', function () { ` ); - expect(running[getCallNode(A)]).toBe(6); - expect(self[getCallNode(A)]).toBe(2); - - expect(running[getCallNode(A, B)]).toBe(4); - expect(self[getCallNode(A, B)]).toBe(4); + expect(getSelfAndTotal(A)).toEqual({ self: 2, total: 6 }); + expect(getSelfAndTotal(A, B)).toEqual({ self: 4, total: 4 }); // This is the last sample, which is deduced to be the interval length. - expect(running[getCallNode(C)]).toBe(profile.meta.interval); - expect(self[getCallNode(C)]).toBe(profile.meta.interval); + const interval = profile.meta.interval; + expect(getSelfAndTotal(C)).toEqual({ self: interval, total: interval }); }); it('computes traced timing for an inverted tree', function () { const { funcNames: { A, B, C }, - getCallNode, - running, - // Rename self to make the assertions more readable. - self: self___, + getSelfAndTotal, } = setup( { inverted: true }, ` @@ -3356,23 +3350,12 @@ describe('traced timing', function () { // Running: [ 1, 4, 4, 1.5, 1, 1 ] // Self: [ 1, 4, 0, 1.5, 0, 0 ] - expect(running[getCallNode(A)]).toBe(1); - expect(self___[getCallNode(A)]).toBe(1); - - expect(running[getCallNode(B)]).toBe(4); - expect(self___[getCallNode(B)]).toBe(4); - - expect(running[getCallNode(B, A)]).toBe(4); - expect(self___[getCallNode(B, A)]).toBe(0); - - expect(running[getCallNode(C)]).toBe(1.5); - expect(self___[getCallNode(C)]).toBe(1.5); - - expect(running[getCallNode(C, B)]).toBe(1); - expect(self___[getCallNode(C, B)]).toBe(0); - - expect(running[getCallNode(C, B, A)]).toBe(1); - expect(self___[getCallNode(C, B, A)]).toBe(0); + expect(getSelfAndTotal(A)).toEqual({ self: 1, total: 1 }); + expect(getSelfAndTotal(B)).toEqual({ self: 4, total: 4 }); + expect(getSelfAndTotal(B, A)).toEqual({ self: 0, total: 4 }); + expect(getSelfAndTotal(C)).toEqual({ self: 1.5, total: 1.5 }); + expect(getSelfAndTotal(C, B)).toEqual({ self: 0, total: 1 }); + expect(getSelfAndTotal(C, B, A)).toEqual({ self: 0, total: 1 }); }); it('does not compute traced timing for other types', function () { @@ -3393,9 +3376,7 @@ describe('traced timing', function () { it('computes traced timing based on the preview selection', function () { const { funcNames: { A, B, C }, - getCallNode, - running, - self, + getSelfAndTotal, profile, } = setup( { inverted: false, previewSelection: { start: 1, end: 5.5 } }, @@ -3411,16 +3392,15 @@ describe('traced timing', function () { // the second sample will have a "traced duration" of the interval, because // it's the last sample in the range. - expect(running[getCallNode(A)]).toBe(4 + profile.meta.interval); - expect(self[getCallNode(A)]).toBe(profile.meta.interval); - - expect(running[getCallNode(A, B)]).toBe(4); - expect(self[getCallNode(A, B)]).toBe(4); + expect(getSelfAndTotal(A)).toEqual({ + self: profile.meta.interval, + total: 4 + profile.meta.interval, + }); + expect(getSelfAndTotal(A, B)).toEqual({ self: 4, total: 4 }); // Call node [C] is fully outside the preview range, so we should have no // traced duration for it. - expect(running[getCallNode(C)]).toBe(0); - expect(self[getCallNode(C)]).toBe(0); + expect(getSelfAndTotal(C)).toEqual({ self: 0, total: 0 }); }); }); diff --git a/src/types/profile-derived.js b/src/types/profile-derived.js index 841af62a39..d5e67b4791 100644 --- a/src/types/profile-derived.js +++ b/src/types/profile-derived.js @@ -654,6 +654,16 @@ export type ProfileFilterPageData = {| favicon: string | null, |}; +/** + * The self and total time, usually for a single call node. + * As with most places where the terms "self" and "total" are used, the meaning + * of the numbers depends on the context: + * - When used for "traced" timing, the values are Milliseconds. + * - Otherwise, the values are in the same unit as the sample weight type. For + * example, they could be sample counts, weights, or bytes. + */ +export type SelfAndTotal = {| self: number, total: number |}; + /** * This struct contains the traced timing for each call node. The arrays are indexed * by the CallNodeIndex, and the values in the Float32Arrays are Milliseconds. The From 5400136bffbe12b793c764791f9eeab6e00dcdb7 Mon Sep 17 00:00:00 2001 From: Markus Stange Date: Wed, 17 Jan 2024 15:08:19 -0500 Subject: [PATCH 26/55] Break _getStackSelf into two functions. --- src/profile-logic/call-tree.js | 116 +++++++++++++++------------------ src/types/profile-derived.js | 9 +++ 2 files changed, 61 insertions(+), 64 deletions(-) diff --git a/src/profile-logic/call-tree.js b/src/profile-logic/call-tree.js index 8d04a4c037..97fc59c644 100644 --- a/src/profile-logic/call-tree.js +++ b/src/profile-logic/call-tree.js @@ -30,6 +30,7 @@ import type { SamplesTable, ExtraBadgeInfo, BottomBoxInfo, + CallNodeLeafAndSummary, } from 'firefox-profiler/types'; import ExtensionIcon from '../../res/img/svg/extension.svg'; @@ -414,22 +415,18 @@ export class CallTree { } } -function _getInvertedStackSelf( - // The samples could either be a SamplesTable, or a JsAllocationsTable. - samples: SamplesLikeTable, - callNodeTable: CallNodeTable, - sampleIndexToCallNodeIndex: Array -): { - // In an inverted profile, all the amount of self unit (time, bytes, count, etc.) is - // accounted to the root nodes. So `callNodeSelf` will be 0 for all non-root nodes. - callNodeSelf: Float32Array, - // This property stores the amount of unit (time, bytes, count, etc.) spent in the - // stacks' leaf nodes. Later these values will make it possible to compute the - // total for all nodes by summing up the values up the tree. +// In an inverted profile, all the amount of self unit (time, bytes, count, etc.) is +// accounted to the root nodes. So `callNodeSelf` will be 0 for all non-root nodes. +function _getInvertedCallNodeSelf( callNodeLeaf: Float32Array, -} { + callNodeTable: CallNodeTable +): Float32Array { // Compute an array that maps the callNodeIndex to its root. const callNodeToRoot = new Int32Array(callNodeTable.length); + + // Compute the self time during the same loop. + const callNodeSelf = new Float32Array(callNodeTable.length); + for ( let callNodeIndex = 0; callNodeIndex < callNodeTable.length; @@ -449,41 +446,22 @@ function _getInvertedStackSelf( // recursively is the value we're looking for. callNodeToRoot[callNodeIndex] = callNodeToRoot[prefixCallNode]; } + callNodeSelf[callNodeToRoot[callNodeIndex]] += callNodeLeaf[callNodeIndex]; } - // Calculate the timing information by going through each sample. - const callNodeSelf = new Float32Array(callNodeTable.length); - const callNodeLeaf = new Float32Array(callNodeTable.length); - for ( - let sampleIndex = 0; - sampleIndex < sampleIndexToCallNodeIndex.length; - sampleIndex++ - ) { - const callNodeIndex = sampleIndexToCallNodeIndex[sampleIndex]; - if (callNodeIndex !== null) { - const rootIndex = callNodeToRoot[callNodeIndex]; - const weight = samples.weight ? samples.weight[sampleIndex] : 1; - callNodeSelf[rootIndex] += weight; - callNodeLeaf[callNodeIndex] += weight; - } - } - - return { callNodeSelf, callNodeLeaf }; + return callNodeSelf; } /** - * This is a helper function to get the stack timings for un-inverted call trees. + * Compute the leaf time for each call node, and the sum of the absolute leaf + * values. */ -function _getStackSelf( +function _getCallNodeLeafAndSummary( samples: SamplesLikeTable, callNodeTable: CallNodeTable, sampleIndexToCallNodeIndex: Array -): { - callNodeSelf: Float32Array, // Milliseconds[] - callNodeLeaf: Float32Array, // Milliseconds[] -} { - const callNodeSelf = new Float32Array(callNodeTable.length); - +): CallNodeLeafAndSummary { + const callNodeLeaf = new Float32Array(callNodeTable.length); for ( let sampleIndex = 0; sampleIndex < sampleIndexToCallNodeIndex.length; @@ -492,11 +470,23 @@ function _getStackSelf( const callNodeIndex = sampleIndexToCallNodeIndex[sampleIndex]; if (callNodeIndex !== null) { const weight = samples.weight ? samples.weight[sampleIndex] : 1; - callNodeSelf[callNodeIndex] += weight; + callNodeLeaf[callNodeIndex] += weight; } } - return { callNodeSelf, callNodeLeaf: callNodeSelf }; + // Workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=1858310 + const abs = Math.abs; + + let rootTotalSummary = 0; + for ( + let callNodeIndex = 0; + callNodeIndex < callNodeTable.length; + callNodeIndex++ + ) { + rootTotalSummary += abs(callNodeLeaf[callNodeIndex]); + } + + return { callNodeLeaf, rootTotalSummary }; } /** @@ -511,22 +501,25 @@ export function computeCallTreeTimings( samples: SamplesLikeTable, sampleIndexToCallNodeIndex: Array, callNodeInfo: CallNodeInfo, - invertCallstack: boolean + _invertCallstack: boolean ): CallTreeTimings { const callNodeTable = callNodeInfo.getCallNodeTable(); - // Inverted trees need a different method for computing the timing. - const { callNodeSelf, callNodeLeaf } = invertCallstack - ? _getInvertedStackSelf(samples, callNodeTable, sampleIndexToCallNodeIndex) - : _getStackSelf(samples, callNodeTable, sampleIndexToCallNodeIndex); + const { callNodeLeaf, rootTotalSummary } = _getCallNodeLeafAndSummary( + samples, + callNodeTable, + sampleIndexToCallNodeIndex + ); + + // The self values depend on whether the call tree is inverted: In an inverted + // tree, all the self time is in the roots. + const callNodeSelf = callNodeInfo.isInverted() + ? _getInvertedCallNodeSelf(callNodeLeaf, callNodeTable) + : callNodeLeaf; // Compute the following variables: const callNodeTotalSummary = new Float32Array(callNodeTable.length); const callNodeHasChildren = new Uint8Array(callNodeTable.length); - let rootTotalSummary = 0; - - // Workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=1858310 - const abs = Math.abs; // We loop the call node table in reverse, so that we find the children // before their parents, and the total is known at the time we reach a @@ -537,7 +530,6 @@ export function computeCallTreeTimings( callNodeIndex-- ) { callNodeTotalSummary[callNodeIndex] += callNodeLeaf[callNodeIndex]; - rootTotalSummary += abs(callNodeLeaf[callNodeIndex]); const hasChildren = callNodeHasChildren[callNodeIndex] !== 0; const hasTotalValue = callNodeTotalSummary[callNodeIndex] !== 0; @@ -676,7 +668,7 @@ export function computeTracedTiming( samples: SamplesLikeTable, callNodeInfo: CallNodeInfo, interval: Milliseconds, - invertCallstack: boolean + _invertCallstack: boolean ): TracedTiming | null { const callNodeTable = callNodeInfo.getCallNodeTable(); const stackIndexToCallNodeIndex = callNodeInfo.getStackIndexToCallNodeIndex(); @@ -710,18 +702,14 @@ export function computeTracedTiming( samples.stack, stackIndexToCallNodeIndex ); - // Inverted trees need a different method for computing the timing. - const { callNodeSelf, callNodeLeaf } = invertCallstack - ? _getInvertedStackSelf( - samplesWithWeight, - callNodeTable, - sampleIndexToCallNodeIndex - ) - : _getStackSelf( - samplesWithWeight, - callNodeTable, - sampleIndexToCallNodeIndex - ); + const { callNodeLeaf } = _getCallNodeLeafAndSummary( + samplesWithWeight, + callNodeTable, + sampleIndexToCallNodeIndex + ); + const callNodeSelf = callNodeInfo.isInverted() + ? _getInvertedCallNodeSelf(callNodeLeaf, callNodeTable) + : callNodeLeaf; // Compute the following variables: const callNodeTotalSummary = new Float32Array(callNodeTable.length); diff --git a/src/types/profile-derived.js b/src/types/profile-derived.js index d5e67b4791..b524d1162d 100644 --- a/src/types/profile-derived.js +++ b/src/types/profile-derived.js @@ -654,6 +654,15 @@ export type ProfileFilterPageData = {| favicon: string | null, |}; +export type CallNodeLeafAndSummary = {| + // This property stores the amount of unit (time, bytes, count, etc.) spent in the + // stacks' leaf nodes. + callNodeLeaf: Float32Array, + // The sum of absolute values in callNodeLeaf. + // This is used for computing the percentages displayed in the call tree. + rootTotalSummary: number, +|}; + /** * The self and total time, usually for a single call node. * As with most places where the terms "self" and "total" are used, the meaning From e6a7b6eb343c39d22379947406529fc8103e3ac5 Mon Sep 17 00:00:00 2001 From: Markus Stange Date: Wed, 17 Jan 2024 15:13:51 -0500 Subject: [PATCH 27/55] Fold CallNodeSummary into CallTreeTimings. --- src/profile-logic/call-tree.js | 47 ++++------- src/profile-logic/flame-graph.js | 3 +- .../__snapshots__/profile-view.test.js.snap | 82 +++++++++++-------- src/test/unit/profile-tree.test.js | 8 +- 4 files changed, 69 insertions(+), 71 deletions(-) diff --git a/src/profile-logic/call-tree.js b/src/profile-logic/call-tree.js index 97fc59c644..0cc2749e3a 100644 --- a/src/profile-logic/call-tree.js +++ b/src/profile-logic/call-tree.js @@ -40,14 +40,12 @@ import * as ProfileData from './profile-data'; import type { CallTreeSummaryStrategy } from '../types/actions'; type CallNodeChildren = IndexIntoCallNodeTable[]; -type CallNodeSummary = { + +export type CallTreeTimings = { + callNodeHasChildren: Uint8Array, self: Float32Array, leaf: Float32Array, total: Float32Array, -}; -export type CallTreeTimings = { - callNodeHasChildren: Uint8Array, - callNodeSummary: CallNodeSummary, rootTotalSummary: number, }; @@ -72,7 +70,7 @@ export class CallTree { _categories: CategoryList; _callNodeInfo: CallNodeInfo; _callNodeTable: CallNodeTable; - _callNodeSummary: CallNodeSummary; + _callTreeTimings: CallTreeTimings; _callNodeHasChildren: Uint8Array; // A table column matching the callNodeTable _thread: Thread; _rootTotalSummary: number; @@ -87,19 +85,17 @@ export class CallTree { thread: Thread, categories: CategoryList, callNodeInfo: CallNodeInfo, - callNodeSummary: CallNodeSummary, - callNodeHasChildren: Uint8Array, - rootTotalSummary: number, + callTreeTimings: CallTreeTimings, isHighPrecision: boolean, weightType: WeightType ) { this._categories = categories; this._callNodeInfo = callNodeInfo; this._callNodeTable = callNodeInfo.getCallNodeTable(); - this._callNodeSummary = callNodeSummary; - this._callNodeHasChildren = callNodeHasChildren; + this._callTreeTimings = callTreeTimings; + this._callNodeHasChildren = callTreeTimings.callNodeHasChildren; this._thread = thread; - this._rootTotalSummary = rootTotalSummary; + this._rootTotalSummary = callTreeTimings.rootTotalSummary; this._displayDataByIndex = new Map(); this._children = []; this._isHighPrecision = isHighPrecision; @@ -134,7 +130,7 @@ export class CallTree { childCallNodeIndex = this._callNodeTable.nextSibling[childCallNodeIndex] ) { const childTotalSummary = - this._callNodeSummary.total[childCallNodeIndex]; + this._callTreeTimings.total[childCallNodeIndex]; const childHasChildren = this._callNodeHasChildren[childCallNodeIndex]; if (childTotalSummary !== 0 || childHasChildren !== 0) { @@ -143,8 +139,8 @@ export class CallTree { } children.sort( (a, b) => - Math.abs(this._callNodeSummary.total[b]) - - Math.abs(this._callNodeSummary.total[a]) + Math.abs(this._callTreeTimings.total[b]) - + Math.abs(this._callTreeTimings.total[a]) ); this._children[callNodeIndex] = children; } @@ -188,9 +184,9 @@ export class CallTree { const funcName = this._thread.stringTable.getString( this._thread.funcTable.name[funcIndex] ); - const total = this._callNodeSummary.total[callNodeIndex]; + const total = this._callTreeTimings.total[callNodeIndex]; const totalRelative = total / this._rootTotalSummary; - const self = this._callNodeSummary.self[callNodeIndex]; + const self = this._callTreeTimings.self[callNodeIndex]; const selfRelative = self / this._rootTotalSummary; return { @@ -404,7 +400,7 @@ export class CallTree { let maxNode = -1; let maxAbs = 0; for (let nodeIndex = callNodeIndex; nodeIndex < rangeEnd; nodeIndex++) { - const nodeLeaf = Math.abs(this._callNodeSummary.leaf[nodeIndex]); + const nodeLeaf = Math.abs(this._callTreeTimings.leaf[nodeIndex]); if (maxNode === -1 || nodeLeaf > maxAbs) { maxNode = nodeIndex; maxAbs = nodeLeaf; @@ -546,11 +542,9 @@ export function computeCallTreeTimings( } return { - callNodeSummary: { - self: callNodeSelf, - leaf: callNodeLeaf, - total: callNodeTotalSummary, - }, + self: callNodeSelf, + leaf: callNodeLeaf, + total: callNodeTotalSummary, callNodeHasChildren, rootTotalSummary, }; @@ -567,16 +561,11 @@ export function getCallTree( weightType: WeightType ): CallTree { return timeCode('getCallTree', () => { - const { callNodeSummary, callNodeHasChildren, rootTotalSummary } = - callTreeTimings; - return new CallTree( thread, categories, callNodeInfo, - callNodeSummary, - callNodeHasChildren, - rootTotalSummary, + callTreeTimings, Boolean(thread.isJsTracer), weightType ); diff --git a/src/profile-logic/flame-graph.js b/src/profile-logic/flame-graph.js index 4d85d96774..fe74e4e25a 100644 --- a/src/profile-logic/flame-graph.js +++ b/src/profile-logic/flame-graph.js @@ -231,8 +231,7 @@ export function getFlameGraphTiming( callNodeTable: CallNodeTable, callTreeTimings: CallTreeTimings ): FlameGraphTiming { - const { callNodeSummary, rootTotalSummary } = callTreeTimings; - const { total, self } = callNodeSummary; + const { total, self, rootTotalSummary } = callTreeTimings; const { prefix } = callNodeTable; // This is where we build up the return value, one row at a time. diff --git a/src/test/store/__snapshots__/profile-view.test.js.snap b/src/test/store/__snapshots__/profile-view.test.js.snap index b112486454..ba34b58ba5 100644 --- a/src/test/store/__snapshots__/profile-view.test.js.snap +++ b/src/test/store/__snapshots__/profile-view.test.js.snap @@ -2433,41 +2433,6 @@ CallTree { 8, ], }, - "_callNodeSummary": Object { - "leaf": Float32Array [ - 0, - 0, - 0, - 0, - 0, - 0, - 2, - 0, - 0, - ], - "self": Float32Array [ - 0, - 0, - 0, - 0, - 0, - 0, - 2, - 0, - 0, - ], - "total": Float32Array [ - 2, - 2, - 0, - 0, - 0, - 2, - 2, - 0, - 0, - ], - }, "_callNodeTable": Object { "category": Int32Array [ 0, @@ -2571,6 +2536,53 @@ CallTree { 9, ], }, + "_callTreeTimings": Object { + "callNodeHasChildren": Uint8Array [ + 1, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + ], + "leaf": Float32Array [ + 0, + 0, + 0, + 0, + 0, + 0, + 2, + 0, + 0, + ], + "rootTotalSummary": 2, + "self": Float32Array [ + 0, + 0, + 0, + 0, + 0, + 0, + 2, + 0, + 0, + ], + "total": Float32Array [ + 2, + 2, + 0, + 0, + 0, + 2, + 2, + 0, + 0, + ], + }, "_categories": Array [ Object { "color": "grey", diff --git a/src/test/unit/profile-tree.test.js b/src/test/unit/profile-tree.test.js index d0d6770716..108aa16f53 100644 --- a/src/test/unit/profile-tree.test.js +++ b/src/test/unit/profile-tree.test.js @@ -82,11 +82,9 @@ describe('unfiltered call tree', function () { ).toEqual({ rootTotalSummary: 3, callNodeHasChildren: new Uint8Array([1, 1, 1, 1, 0, 1, 0, 1, 0]), - callNodeSummary: { - self: new Float32Array([0, 0, 0, 0, 1, 0, 1, 0, 1]), - leaf: new Float32Array([0, 0, 0, 0, 1, 0, 1, 0, 1]), - total: new Float32Array([3, 3, 2, 1, 1, 1, 1, 1, 1]), - }, + self: new Float32Array([0, 0, 0, 0, 1, 0, 1, 0, 1]), + leaf: new Float32Array([0, 0, 0, 0, 1, 0, 1, 0, 1]), + total: new Float32Array([3, 3, 2, 1, 1, 1, 1, 1, 1]), }); }); }); From f8152a7df269f01e30c85c5f737a2da0d6035b5b Mon Sep 17 00:00:00 2001 From: Markus Stange Date: Wed, 17 Jan 2024 15:29:12 -0500 Subject: [PATCH 28/55] Feed CallNodeLeafAndSummary into computeCallTreeTimings. --- src/profile-logic/call-tree.js | 35 +++++---------- src/selectors/per-thread/stack-sample.js | 11 ++--- src/test/fixtures/utils.js | 15 ++++--- src/test/unit/profile-tree.test.js | 54 ++++++++++++++---------- 4 files changed, 56 insertions(+), 59 deletions(-) diff --git a/src/profile-logic/call-tree.js b/src/profile-logic/call-tree.js index 0cc2749e3a..7522b2f3a2 100644 --- a/src/profile-logic/call-tree.js +++ b/src/profile-logic/call-tree.js @@ -452,12 +452,12 @@ function _getInvertedCallNodeSelf( * Compute the leaf time for each call node, and the sum of the absolute leaf * values. */ -function _getCallNodeLeafAndSummary( +export function computeCallNodeLeafAndSummary( samples: SamplesLikeTable, - callNodeTable: CallNodeTable, - sampleIndexToCallNodeIndex: Array + sampleIndexToCallNodeIndex: Array, + callNodeCount: number ): CallNodeLeafAndSummary { - const callNodeLeaf = new Float32Array(callNodeTable.length); + const callNodeLeaf = new Float32Array(callNodeCount); for ( let sampleIndex = 0; sampleIndex < sampleIndexToCallNodeIndex.length; @@ -474,11 +474,7 @@ function _getCallNodeLeafAndSummary( const abs = Math.abs; let rootTotalSummary = 0; - for ( - let callNodeIndex = 0; - callNodeIndex < callNodeTable.length; - callNodeIndex++ - ) { + for (let callNodeIndex = 0; callNodeIndex < callNodeCount; callNodeIndex++) { rootTotalSummary += abs(callNodeLeaf[callNodeIndex]); } @@ -488,24 +484,13 @@ function _getCallNodeLeafAndSummary( /** * This computes all of the count and timing information displayed in the calltree. * It takes into account both the normal tree, and the inverted tree. - * - * Note: The "timionmgs" could have a number of different meanings based on the - * what type of weight is in the SamplesLikeTable. For instance, it could be - * milliseconds, sample counts, or bytes. */ export function computeCallTreeTimings( - samples: SamplesLikeTable, - sampleIndexToCallNodeIndex: Array, callNodeInfo: CallNodeInfo, - _invertCallstack: boolean + callNodeLeafAndSummary: CallNodeLeafAndSummary ): CallTreeTimings { const callNodeTable = callNodeInfo.getCallNodeTable(); - - const { callNodeLeaf, rootTotalSummary } = _getCallNodeLeafAndSummary( - samples, - callNodeTable, - sampleIndexToCallNodeIndex - ); + const { callNodeLeaf, rootTotalSummary } = callNodeLeafAndSummary; // The self values depend on whether the call tree is inverted: In an inverted // tree, all the self time is in the roots. @@ -691,10 +676,10 @@ export function computeTracedTiming( samples.stack, stackIndexToCallNodeIndex ); - const { callNodeLeaf } = _getCallNodeLeafAndSummary( + const { callNodeLeaf } = computeCallNodeLeafAndSummary( samplesWithWeight, - callNodeTable, - sampleIndexToCallNodeIndex + sampleIndexToCallNodeIndex, + callNodeTable.length ); const callNodeSelf = callNodeInfo.isInverted() ? _getInvertedCallNodeSelf(callNodeLeaf, callNodeTable) diff --git a/src/selectors/per-thread/stack-sample.js b/src/selectors/per-thread/stack-sample.js index 7bd029ccf5..1811f62b95 100644 --- a/src/selectors/per-thread/stack-sample.js +++ b/src/selectors/per-thread/stack-sample.js @@ -304,19 +304,20 @@ export function getStackAndSampleSelectorsPerThread( const getCallTreeTimings: Selector = createSelector( threadSelectors.getPreviewFilteredSamplesForCallTree, getCallNodeInfo, - ProfileSelectors.getProfileInterval, - UrlState.getInvertCallstack, - (samples, callNodeInfo, interval, invertCallStack) => { + (samples, callNodeInfo) => { const sampleIndexToCallNodeIndex = ProfileData.getSampleIndexToCallNodeIndex( samples.stack, callNodeInfo.getStackIndexToCallNodeIndex() ); - return CallTree.computeCallTreeTimings( + const callNodeLeafAndSummary = CallTree.computeCallNodeLeafAndSummary( samples, sampleIndexToCallNodeIndex, + callNodeInfo.getCallNodeTable().length + ); + return CallTree.computeCallTreeTimings( callNodeInfo, - invertCallStack + callNodeLeafAndSummary ); } ); diff --git a/src/test/fixtures/utils.js b/src/test/fixtures/utils.js index 8b329e7ad4..de1db874b2 100644 --- a/src/test/fixtures/utils.js +++ b/src/test/fixtures/utils.js @@ -4,6 +4,7 @@ // @flow import { getCallTree, + computeCallNodeLeafAndSummary, computeCallTreeTimings, type CallTree, } from 'firefox-profiler/profile-logic/call-tree'; @@ -128,13 +129,15 @@ export function callTreeFromProfile( defaultCategory ); const callTreeTimings = computeCallTreeTimings( - thread.samples, - getSampleIndexToCallNodeIndex( - thread.samples.stack, - callNodeInfo.getStackIndexToCallNodeIndex() - ), callNodeInfo, - false + computeCallNodeLeafAndSummary( + thread.samples, + getSampleIndexToCallNodeIndex( + thread.samples.stack, + callNodeInfo.getStackIndexToCallNodeIndex() + ), + callNodeInfo.getCallNodeTable().length + ) ); return getCallTree( thread, diff --git a/src/test/unit/profile-tree.test.js b/src/test/unit/profile-tree.test.js index 108aa16f53..eb81d70663 100644 --- a/src/test/unit/profile-tree.test.js +++ b/src/test/unit/profile-tree.test.js @@ -8,6 +8,7 @@ import { } from '../fixtures/profiles/processed-profile'; import { getCallTree, + computeCallNodeLeafAndSummary, computeCallTreeTimings, } from '../../profile-logic/call-tree'; import { computeFlameGraphRows } from '../../profile-logic/flame-graph'; @@ -69,17 +70,18 @@ describe('unfiltered call tree', function () { ); it('yields expected results', function () { - expect( - computeCallTreeTimings( + const callTreeTimings = computeCallTreeTimings( + callNodeInfo, + computeCallNodeLeafAndSummary( thread.samples, getSampleIndexToCallNodeIndex( thread.samples.stack, callNodeInfo.getStackIndexToCallNodeIndex() ), - callNodeInfo, - false + callNodeInfo.getCallNodeTable().length ) - ).toEqual({ + ); + expect(callTreeTimings).toEqual({ rootTotalSummary: 3, callNodeHasChildren: new Uint8Array([1, 1, 1, 1, 0, 1, 0, 1, 0]), self: new Float32Array([0, 0, 0, 0, 1, 0, 1, 0, 1]), @@ -431,13 +433,15 @@ describe('inverted call tree', function () { defaultCategory ); const callTreeTimings = computeCallTreeTimings( - thread.samples, - getSampleIndexToCallNodeIndex( - thread.samples.stack, - callNodeInfo.getStackIndexToCallNodeIndex() - ), callNodeInfo, - false + computeCallNodeLeafAndSummary( + thread.samples, + getSampleIndexToCallNodeIndex( + thread.samples.stack, + callNodeInfo.getStackIndexToCallNodeIndex() + ), + callNodeInfo.getCallNodeTable().length + ) ); const callTree = getCallTree( thread, @@ -469,13 +473,15 @@ describe('inverted call tree', function () { defaultCategory ); const invertedCallTreeTimings = computeCallTreeTimings( - thread.samples, - getSampleIndexToCallNodeIndex( - thread.samples.stack, - invertedCallNodeInfo.getStackIndexToCallNodeIndex() - ), invertedCallNodeInfo, - true + computeCallNodeLeafAndSummary( + thread.samples, + getSampleIndexToCallNodeIndex( + thread.samples.stack, + invertedCallNodeInfo.getStackIndexToCallNodeIndex() + ), + invertedCallNodeInfo.getCallNodeTable().length + ) ); const invertedCallTree = getCallTree( thread, @@ -619,13 +625,15 @@ describe('diffing trees', function () { defaultCategory ); const callTreeTimings = computeCallTreeTimings( - thread.samples, - getSampleIndexToCallNodeIndex( - thread.samples.stack, - callNodeInfo.getStackIndexToCallNodeIndex() - ), callNodeInfo, - false + computeCallNodeLeafAndSummary( + thread.samples, + getSampleIndexToCallNodeIndex( + thread.samples.stack, + callNodeInfo.getStackIndexToCallNodeIndex() + ), + callNodeInfo.getCallNodeTable().length + ) ); expect(callTreeTimings.rootTotalSummary).toBe(12); }); From b13edbb3f8f4a39913d110b63318a84379407fa1 Mon Sep 17 00:00:00 2001 From: Markus Stange Date: Wed, 17 Jan 2024 15:53:28 -0500 Subject: [PATCH 29/55] Use computeCallTreeTimings for traced timing computation. --- src/components/flame-graph/Canvas.js | 10 +-- src/components/flame-graph/FlameGraph.js | 8 ++- src/profile-logic/call-tree.js | 89 +++++++----------------- src/selectors/per-thread/stack-sample.js | 37 +++++++--- src/test/store/profile-view.test.js | 8 +-- src/types/profile-derived.js | 11 --- 6 files changed, 67 insertions(+), 96 deletions(-) diff --git a/src/components/flame-graph/Canvas.js b/src/components/flame-graph/Canvas.js index 22b1c54226..d730d71313 100644 --- a/src/components/flame-graph/Canvas.js +++ b/src/components/flame-graph/Canvas.js @@ -33,7 +33,6 @@ import type { CallTreeSummaryStrategy, WeightType, SamplesLikeTable, - TracedTiming, InnerWindowID, Page, } from 'firefox-profiler/types'; @@ -49,7 +48,10 @@ import type { ChartCanvasHoverInfo, } from '../shared/chart/Canvas'; -import type { CallTree } from 'firefox-profiler/profile-logic/call-tree'; +import type { + CallTree, + CallTreeTimings, +} from 'firefox-profiler/profile-logic/call-tree'; export type OwnProps = {| +thread: Thread, @@ -75,7 +77,7 @@ export type OwnProps = {| +callTreeSummaryStrategy: CallTreeSummaryStrategy, +samples: SamplesLikeTable, +unfilteredSamples: SamplesLikeTable, - +tracedTiming: TracedTiming | null, + +tracedTiming: CallTreeTimings | null, +displayImplementation: boolean, +displayStackType: boolean, |}; @@ -391,7 +393,7 @@ class FlameGraphCanvasImpl extends React.PureComponent { const time = formatCallNodeNumberWithUnit( 'tracing-ms', false, - tracedTiming.running[callNodeIndex] + tracedTiming.total[callNodeIndex] ); percentage = `${time} (${percentage})`; } diff --git a/src/components/flame-graph/FlameGraph.js b/src/components/flame-graph/FlameGraph.js index 35769154e3..f80531e8fd 100644 --- a/src/components/flame-graph/FlameGraph.js +++ b/src/components/flame-graph/FlameGraph.js @@ -42,7 +42,6 @@ import type { CallTreeSummaryStrategy, CallNodeInfo, IndexIntoCallNodeTable, - TracedTiming, ThreadsKey, InnerWindowID, Page, @@ -50,7 +49,10 @@ import type { import type { FlameGraphTiming } from 'firefox-profiler/profile-logic/flame-graph'; -import type { CallTree } from 'firefox-profiler/profile-logic/call-tree'; +import type { + CallTree, + CallTreeTimings, +} from 'firefox-profiler/profile-logic/call-tree'; import type { ConnectedProps } from 'firefox-profiler/utils/connect'; @@ -87,7 +89,7 @@ type StateProps = {| +callTreeSummaryStrategy: CallTreeSummaryStrategy, +samples: SamplesLikeTable, +unfilteredSamples: SamplesLikeTable, - +tracedTiming: TracedTiming | null, + +tracedTiming: CallTreeTimings | null, +displayImplementation: boolean, +displayStackType: boolean, |}; diff --git a/src/profile-logic/call-tree.js b/src/profile-logic/call-tree.js index 7522b2f3a2..0f80f8c284 100644 --- a/src/profile-logic/call-tree.js +++ b/src/profile-logic/call-tree.js @@ -6,7 +6,6 @@ import { oneLine } from 'common-tags'; import { timeCode } from '../utils/time-code'; import { - getSampleIndexToCallNodeIndex, getOriginAnnotationForFunc, getCategoryPairLabel, getBottomBoxInfoForCallNode, @@ -26,8 +25,6 @@ import type { CallNodeData, CallNodeDisplayData, Milliseconds, - TracedTiming, - SamplesTable, ExtraBadgeInfo, BottomBoxInfo, CallNodeLeafAndSummary, @@ -628,7 +625,7 @@ export function extractSamplesLikeTable( } /** - * This function is extremely similar to computeCallTreeTimings, + * This function is extremely similar to computeCallNodeLeafAndSummary, * but is specialized for converting sample counts into traced timing. Samples * don't have duration information associated with them, it's mostly how long they * were observed to be running. This function computes the timing the exact same @@ -638,15 +635,12 @@ export function extractSamplesLikeTable( * did not agree. In order to remove confusion, we can show the sample counts, * plus the traced timing, which is a compromise between correctness, and consistency. */ -export function computeTracedTiming( +export function computeCallNodeTracedLeafAndSummary( samples: SamplesLikeTable, - callNodeInfo: CallNodeInfo, - interval: Milliseconds, - _invertCallstack: boolean -): TracedTiming | null { - const callNodeTable = callNodeInfo.getCallNodeTable(); - const stackIndexToCallNodeIndex = callNodeInfo.getStackIndexToCallNodeIndex(); - + sampleIndexToCallNodeIndex: Array, + callNodeCount: number, + interval: Milliseconds +): CallNodeLeafAndSummary | null { if (samples.weightType !== 'samples' || samples.weight) { // Only compute for the samples weight types that have no weights. If a samples // table has weights then it's a diff profile. Currently, we aren't calculating @@ -658,63 +652,28 @@ export function computeTracedTiming( return null; } - // Compute the timing duration, which is the time between this sample and the next. - const weight = []; - for (let sampleIndex = 0; sampleIndex < samples.length - 1; sampleIndex++) { - weight.push(samples.time[sampleIndex + 1] - samples.time[sampleIndex]); - } - if (samples.length > 0) { - // Use the sampling interval for the last sample. - weight.push(interval); - } - const samplesWithWeight: SamplesTable = { - ...samples, - weight, - }; - - const sampleIndexToCallNodeIndex = getSampleIndexToCallNodeIndex( - samples.stack, - stackIndexToCallNodeIndex - ); - const { callNodeLeaf } = computeCallNodeLeafAndSummary( - samplesWithWeight, - sampleIndexToCallNodeIndex, - callNodeTable.length - ); - const callNodeSelf = callNodeInfo.isInverted() - ? _getInvertedCallNodeSelf(callNodeLeaf, callNodeTable) - : callNodeLeaf; - - // Compute the following variables: - const callNodeTotalSummary = new Float32Array(callNodeTable.length); - const callNodeHasChildren = new Uint8Array(callNodeTable.length); - - // We loop the call node table in reverse, so that we find the children - // before their parents, and the total time is known at the time we reach a - // node. - for ( - let callNodeIndex = callNodeTable.length - 1; - callNodeIndex >= 0; - callNodeIndex-- - ) { - callNodeTotalSummary[callNodeIndex] += callNodeLeaf[callNodeIndex]; - const hasChildren = callNodeHasChildren[callNodeIndex] !== 0; - const hasTotalValue = callNodeTotalSummary[callNodeIndex] !== 0; + const callNodeLeaf = new Float32Array(callNodeCount); + let rootTotalSummary = 0; - if (!hasChildren && !hasTotalValue) { - continue; + for (let sampleIndex = 0; sampleIndex < samples.length - 1; sampleIndex++) { + const callNodeIndex = sampleIndexToCallNodeIndex[sampleIndex]; + if (callNodeIndex !== null) { + const sampleTracedTime = + samples.time[sampleIndex + 1] - samples.time[sampleIndex]; + callNodeLeaf[callNodeIndex] += sampleTracedTime; + rootTotalSummary += sampleTracedTime; } + } - const prefixCallNode = callNodeTable.prefix[callNodeIndex]; - if (prefixCallNode !== -1) { - callNodeTotalSummary[prefixCallNode] += - callNodeTotalSummary[callNodeIndex]; - callNodeHasChildren[prefixCallNode] = 1; + if (samples.length > 0) { + const callNodeIndex = sampleIndexToCallNodeIndex[samples.length - 1]; + if (callNodeIndex !== null) { + // Use the sampling interval for the last sample. + const sampleTracedTime = interval; + callNodeLeaf[callNodeIndex] += sampleTracedTime; + rootTotalSummary += sampleTracedTime; } } - return { - self: callNodeSelf, - running: callNodeTotalSummary, - }; + return { callNodeLeaf, rootTotalSummary }; } diff --git a/src/selectors/per-thread/stack-sample.js b/src/selectors/per-thread/stack-sample.js index 1811f62b95..1852b81fef 100644 --- a/src/selectors/per-thread/stack-sample.js +++ b/src/selectors/per-thread/stack-sample.js @@ -42,7 +42,6 @@ import type { StartEndRange, Selector, $ReturnType, - TracedTiming, ThreadsKey, SelfAndTotal, } from 'firefox-profiler/types'; @@ -344,13 +343,33 @@ export function getStackAndSampleSelectorsPerThread( getAddressTimings ); - const getTracedTiming: Selector = createSelector( - threadSelectors.getPreviewFilteredSamplesForCallTree, - getCallNodeInfo, - ProfileSelectors.getProfileInterval, - UrlState.getInvertCallstack, - CallTree.computeTracedTiming - ); + const getTracedTiming: Selector = + createSelector( + threadSelectors.getPreviewFilteredSamplesForCallTree, + getCallNodeInfo, + ProfileSelectors.getProfileInterval, + (samples, callNodeInfo, interval) => { + const sampleIndexToCallNodeIndex = + ProfileData.getSampleIndexToCallNodeIndex( + samples.stack, + callNodeInfo.getStackIndexToCallNodeIndex() + ); + const callNodeLeafAndSummary = + CallTree.computeCallNodeTracedLeafAndSummary( + samples, + sampleIndexToCallNodeIndex, + callNodeInfo.getCallNodeTable().length, + interval + ); + if (callNodeLeafAndSummary === null) { + return null; + } + return CallTree.computeCallTreeTimings( + callNodeInfo, + callNodeLeafAndSummary + ); + } + ); const getTracedSelfAndTotalForSelectedCallNode: Selector = createSelector( @@ -360,7 +379,7 @@ export function getStackAndSampleSelectorsPerThread( if (selectedCallNodeIndex === null || tracedTiming === null) { return null; } - const total = tracedTiming.running[selectedCallNodeIndex]; + const total = tracedTiming.total[selectedCallNodeIndex]; const self = tracedTiming.self[selectedCallNodeIndex]; return { total, self }; } diff --git a/src/test/store/profile-view.test.js b/src/test/store/profile-view.test.js index 7578e34ab8..edaaa07eb8 100644 --- a/src/test/store/profile-view.test.js +++ b/src/test/store/profile-view.test.js @@ -3287,7 +3287,7 @@ describe('traced timing', function () { const callNodeInfo = selectedThreadSelectors.getCallNodeInfo(getState()); - const { running, self } = ensureExists( + const { total, self } = ensureExists( selectedThreadSelectors.getTracedTiming(getState()), 'Expected to get a traced timing.' ); @@ -3298,7 +3298,7 @@ describe('traced timing', function () { const callNodeIndex = ensureExists( callNodeInfo.getCallNodeIndexFromPath(callNodePath) ); - return { self: self[callNodeIndex], total: running[callNodeIndex] }; + return { self: self[callNodeIndex], total: total[callNodeIndex] }; }, profile, }; @@ -3347,8 +3347,8 @@ describe('traced timing', function () { ); // This test is a bit hard to assert in a really readable fasshion. - // Running: [ 1, 4, 4, 1.5, 1, 1 ] - // Self: [ 1, 4, 0, 1.5, 0, 0 ] + // total: [ 1, 4, 4, 1.5, 1, 1 ] + // Self: [ 1, 4, 0, 1.5, 0, 0 ] expect(getSelfAndTotal(A)).toEqual({ self: 1, total: 1 }); expect(getSelfAndTotal(B)).toEqual({ self: 4, total: 4 }); diff --git a/src/types/profile-derived.js b/src/types/profile-derived.js index b524d1162d..956ad7a456 100644 --- a/src/types/profile-derived.js +++ b/src/types/profile-derived.js @@ -673,17 +673,6 @@ export type CallNodeLeafAndSummary = {| */ export type SelfAndTotal = {| self: number, total: number |}; -/** - * This struct contains the traced timing for each call node. The arrays are indexed - * by the CallNodeIndex, and the values in the Float32Arrays are Milliseconds. The - * traced timing is computed by summing the distance between samples for a given call - * node. See the `computeTracedTiming` for more details. - */ -export type TracedTiming = {| - +self: Float32Array, - +running: Float32Array, -|}; - /* * Event delay table that holds the pre-processed event delay values and other * statistics about it. From cfed73b86bf3a5a2be08fd1d399806ef5cfbc63b Mon Sep 17 00:00:00 2001 From: Markus Stange Date: Wed, 17 Jan 2024 17:16:05 -0500 Subject: [PATCH 30/55] Use getSampleIndexToCallNodeIndexForPreviewFilteredThread. --- src/selectors/per-thread/stack-sample.js | 25 ++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/src/selectors/per-thread/stack-sample.js b/src/selectors/per-thread/stack-sample.js index 1852b81fef..d10525c03e 100644 --- a/src/selectors/per-thread/stack-sample.js +++ b/src/selectors/per-thread/stack-sample.js @@ -234,6 +234,15 @@ export function getStackAndSampleSelectorsPerThread( ) ); + const getSampleIndexToCallNodeIndexForPreviewFilteredThread: Selector< + Array, + > = createSelector( + (state) => + threadSelectors.getPreviewFilteredSamplesForCallTree(state).stack, + (state) => getCallNodeInfo(state).getStackIndexToCallNodeIndex(), + ProfileData.getSampleIndexToCallNodeIndex + ); + const getSampleIndexToCallNodeIndexForTabFilteredThread: Selector< Array, > = createSelector( @@ -302,13 +311,9 @@ export function getStackAndSampleSelectorsPerThread( const getCallTreeTimings: Selector = createSelector( threadSelectors.getPreviewFilteredSamplesForCallTree, + getSampleIndexToCallNodeIndexForPreviewFilteredThread, getCallNodeInfo, - (samples, callNodeInfo) => { - const sampleIndexToCallNodeIndex = - ProfileData.getSampleIndexToCallNodeIndex( - samples.stack, - callNodeInfo.getStackIndexToCallNodeIndex() - ); + (samples, sampleIndexToCallNodeIndex, callNodeInfo) => { const callNodeLeafAndSummary = CallTree.computeCallNodeLeafAndSummary( samples, sampleIndexToCallNodeIndex, @@ -346,14 +351,10 @@ export function getStackAndSampleSelectorsPerThread( const getTracedTiming: Selector = createSelector( threadSelectors.getPreviewFilteredSamplesForCallTree, + getSampleIndexToCallNodeIndexForPreviewFilteredThread, getCallNodeInfo, ProfileSelectors.getProfileInterval, - (samples, callNodeInfo, interval) => { - const sampleIndexToCallNodeIndex = - ProfileData.getSampleIndexToCallNodeIndex( - samples.stack, - callNodeInfo.getStackIndexToCallNodeIndex() - ); + (samples, sampleIndexToCallNodeIndex, callNodeInfo, interval) => { const callNodeLeafAndSummary = CallTree.computeCallNodeTracedLeafAndSummary( samples, From 10074e36281976924708cf77a6c06e49bcb07fe4 Mon Sep 17 00:00:00 2001 From: Markus Stange Date: Thu, 18 Jan 2024 18:08:43 -0500 Subject: [PATCH 31/55] Split out part of CallTree into CallTreeInternal. This will let us add a second implementation of CallTreeInternal for the inverted tree. What's still in CallTree will be used for both the inverted and the non-inverted tree. --- src/profile-logic/call-tree.js | 174 +++++---- .../__snapshots__/profile-view.test.js.snap | 342 +++++++++++++++--- 2 files changed, 394 insertions(+), 122 deletions(-) diff --git a/src/profile-logic/call-tree.js b/src/profile-logic/call-tree.js index 0f80f8c284..b8e01e0525 100644 --- a/src/profile-logic/call-tree.js +++ b/src/profile-logic/call-tree.js @@ -28,6 +28,7 @@ import type { ExtraBadgeInfo, BottomBoxInfo, CallNodeLeafAndSummary, + SelfAndTotal, } from 'firefox-profiler/types'; import ExtensionIcon from '../../res/img/svg/extension.svg'; @@ -63,18 +64,111 @@ function extractFaviconFromLibname(libname: string): string | null { } } -export class CallTree { - _categories: CategoryList; +interface CallTreeInternal { + hasChildren(callNodeIndex: IndexIntoCallNodeTable): boolean; + createChildren(nodeIndex: IndexIntoCallNodeTable): CallNodeChildren; + createRoots(): CallNodeChildren; + getSelfAndTotal(nodeIndex: IndexIntoCallNodeTable): SelfAndTotal; + findHeaviestPathInSubtree( + callNodeIndex: IndexIntoCallNodeTable + ): CallNodePath; +} + +export class CallTreeInternalImpl implements CallTreeInternal { _callNodeInfo: CallNodeInfo; _callNodeTable: CallNodeTable; _callTreeTimings: CallTreeTimings; _callNodeHasChildren: Uint8Array; // A table column matching the callNodeTable + + constructor(callNodeInfo: CallNodeInfo, callTreeTimings: CallTreeTimings) { + this._callNodeInfo = callNodeInfo; + this._callNodeTable = callNodeInfo.getCallNodeTable(); + this._callTreeTimings = callTreeTimings; + this._callNodeHasChildren = callTreeTimings.callNodeHasChildren; + } + + _getFirstChildIndex( + callNodeIndex: IndexIntoCallNodeTable | -1 + ): IndexIntoCallNodeTable | -1 { + if (callNodeIndex === -1) { + return this._callNodeTable.length !== 0 ? 0 : -1; + } + const subtreeRangeEnd = this._callNodeTable.subtreeRangeEnd[callNodeIndex]; + if (subtreeRangeEnd !== callNodeIndex + 1) { + return callNodeIndex + 1; + } + return -1; + } + + createRoots() { + return this.createChildren(-1); + } + + createChildren(callNodeIndex: IndexIntoCallNodeTable): CallNodeChildren { + const firstChild = this._getFirstChildIndex(callNodeIndex); + const children = []; + for ( + let childCallNodeIndex = firstChild; + childCallNodeIndex !== -1; + childCallNodeIndex = this._callNodeTable.nextSibling[childCallNodeIndex] + ) { + const childTotalSummary = this._callTreeTimings.total[childCallNodeIndex]; + const childHasChildren = this._callNodeHasChildren[childCallNodeIndex]; + + if (childTotalSummary !== 0 || childHasChildren !== 0) { + children.push(childCallNodeIndex); + } + } + children.sort( + (a, b) => + Math.abs(this._callTreeTimings.total[b]) - + Math.abs(this._callTreeTimings.total[a]) + ); + return children; + } + + hasChildren(callNodeIndex: IndexIntoCallNodeTable): boolean { + return this._callNodeHasChildren[callNodeIndex] !== 0; + } + + getSelfAndTotal(callNodeIndex: IndexIntoCallNodeTable): SelfAndTotal { + const self = this._callTreeTimings.self[callNodeIndex]; + const total = this._callTreeTimings.total[callNodeIndex]; + return { self, total }; + } + + findHeaviestPathInSubtree( + callNodeIndex: IndexIntoCallNodeTable + ): CallNodePath { + const rangeEnd = this._callNodeTable.subtreeRangeEnd[callNodeIndex]; + + // Find the call node with the highest leaf time. + let maxNode = -1; + let maxAbs = 0; + for (let nodeIndex = callNodeIndex; nodeIndex < rangeEnd; nodeIndex++) { + const nodeLeaf = Math.abs(this._callTreeTimings.leaf[nodeIndex]); + if (maxNode === -1 || nodeLeaf > maxAbs) { + maxNode = nodeIndex; + maxAbs = nodeLeaf; + } + } + + return this._callNodeInfo.getCallNodePathFromIndex(maxNode); + } +} + +export class CallTree { + _categories: CategoryList; + _internal: CallTreeInternal; + _callNodeInfo: CallNodeInfo; + _callNodeTable: CallNodeTable; _thread: Thread; _rootTotalSummary: number; _displayDataByIndex: Map; // _children is indexed by IndexIntoCallNodeTable. Since they are // integers, using an array directly is faster than going through a Map. _children: Array; + _roots: IndexIntoCallNodeTable[]; _isHighPrecision: boolean; _weightType: WeightType; @@ -82,70 +176,39 @@ export class CallTree { thread: Thread, categories: CategoryList, callNodeInfo: CallNodeInfo, - callTreeTimings: CallTreeTimings, + internal: CallTreeInternal, + rootTotalSummary: number, isHighPrecision: boolean, weightType: WeightType ) { this._categories = categories; + this._internal = internal; this._callNodeInfo = callNodeInfo; this._callNodeTable = callNodeInfo.getCallNodeTable(); - this._callTreeTimings = callTreeTimings; - this._callNodeHasChildren = callTreeTimings.callNodeHasChildren; this._thread = thread; - this._rootTotalSummary = callTreeTimings.rootTotalSummary; + this._rootTotalSummary = rootTotalSummary; this._displayDataByIndex = new Map(); this._children = []; + this._roots = internal.createRoots(); this._isHighPrecision = isHighPrecision; this._weightType = weightType; } - _getFirstChildIndex( - callNodeIndex: IndexIntoCallNodeTable | -1 - ): IndexIntoCallNodeTable | -1 { - if (callNodeIndex === -1) { - return this._callNodeTable.length !== 0 ? 0 : -1; - } - const subtreeRangeEnd = this._callNodeTable.subtreeRangeEnd[callNodeIndex]; - if (subtreeRangeEnd !== callNodeIndex + 1) { - return callNodeIndex + 1; - } - return -1; - } - getRoots() { - return this.getChildren(-1); + return this._roots; } getChildren(callNodeIndex: IndexIntoCallNodeTable): CallNodeChildren { let children = this._children[callNodeIndex]; if (children === undefined) { - children = []; - const firstChild = this._getFirstChildIndex(callNodeIndex); - for ( - let childCallNodeIndex = firstChild; - childCallNodeIndex !== -1; - childCallNodeIndex = this._callNodeTable.nextSibling[childCallNodeIndex] - ) { - const childTotalSummary = - this._callTreeTimings.total[childCallNodeIndex]; - const childHasChildren = this._callNodeHasChildren[childCallNodeIndex]; - - if (childTotalSummary !== 0 || childHasChildren !== 0) { - children.push(childCallNodeIndex); - } - } - children.sort( - (a, b) => - Math.abs(this._callTreeTimings.total[b]) - - Math.abs(this._callTreeTimings.total[a]) - ); + children = this._internal.createChildren(callNodeIndex); this._children[callNodeIndex] = children; } return children; } hasChildren(callNodeIndex: IndexIntoCallNodeTable): boolean { - return this._callNodeHasChildren[callNodeIndex] !== 0; + return this._internal.hasChildren(callNodeIndex); } _addDescendantsToSet( @@ -181,9 +244,9 @@ export class CallTree { const funcName = this._thread.stringTable.getString( this._thread.funcTable.name[funcIndex] ); - const total = this._callTreeTimings.total[callNodeIndex]; + + const { self, total } = this._internal.getSelfAndTotal(callNodeIndex); const totalRelative = total / this._rootTotalSummary; - const self = this._callTreeTimings.self[callNodeIndex]; const selfRelative = self / this._rootTotalSummary; return { @@ -382,30 +445,12 @@ export class CallTree { if (callNodeIndex === null) { return []; } - const heaviestPath = this.findHeaviestPathInSubtree(callNodeIndex); + const heaviestPath = + this._internal.findHeaviestPathInSubtree(callNodeIndex); const startingDepth = this._callNodeTable.depth[callNodeIndex]; const partialPath = heaviestPath.slice(startingDepth); return partialPath.reverse(); } - - findHeaviestPathInSubtree( - callNodeIndex: IndexIntoCallNodeTable - ): CallNodePath { - const rangeEnd = this._callNodeTable.subtreeRangeEnd[callNodeIndex]; - - // Find the call node with the highest leaf time. - let maxNode = -1; - let maxAbs = 0; - for (let nodeIndex = callNodeIndex; nodeIndex < rangeEnd; nodeIndex++) { - const nodeLeaf = Math.abs(this._callTreeTimings.leaf[nodeIndex]); - if (maxNode === -1 || nodeLeaf > maxAbs) { - maxNode = nodeIndex; - maxAbs = nodeLeaf; - } - } - - return this._callNodeInfo.getCallNodePathFromIndex(maxNode); - } } // In an inverted profile, all the amount of self unit (time, bytes, count, etc.) is @@ -547,7 +592,8 @@ export function getCallTree( thread, categories, callNodeInfo, - callTreeTimings, + new CallTreeInternalImpl(callNodeInfo, callTreeTimings), + callTreeTimings.rootTotalSummary, Boolean(thread.isJsTracer), weightType ); diff --git a/src/test/store/__snapshots__/profile-view.test.js.snap b/src/test/store/__snapshots__/profile-view.test.js.snap index ba34b58ba5..a2a9958e8e 100644 --- a/src/test/store/__snapshots__/profile-view.test.js.snap +++ b/src/test/store/__snapshots__/profile-view.test.js.snap @@ -2304,17 +2304,6 @@ CallNodeInfoImpl { exports[`snapshots of selectors/profile matches the last stored run of selectedThreadSelector.getCallTree 1`] = ` CallTree { - "_callNodeHasChildren": Uint8Array [ - 1, - 1, - 0, - 0, - 0, - 1, - 0, - 0, - 0, - ], "_callNodeInfo": CallNodeInfoImpl { "_cache": Map {}, "_callNodeTable": Object { @@ -2536,53 +2525,6 @@ CallTree { 9, ], }, - "_callTreeTimings": Object { - "callNodeHasChildren": Uint8Array [ - 1, - 1, - 0, - 0, - 0, - 1, - 0, - 0, - 0, - ], - "leaf": Float32Array [ - 0, - 0, - 0, - 0, - 0, - 0, - 2, - 0, - 0, - ], - "rootTotalSummary": 2, - "self": Float32Array [ - 0, - 0, - 0, - 0, - 0, - 0, - 2, - 0, - 0, - ], - "total": Float32Array [ - 2, - 2, - 0, - 0, - 0, - 2, - 2, - 0, - 0, - ], - }, "_categories": Array [ Object { "color": "grey", @@ -2643,8 +2585,292 @@ CallTree { ], "_children": Array [], "_displayDataByIndex": Map {}, + "_internal": CallTreeInternalImpl { + "_callNodeHasChildren": Uint8Array [ + 1, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + ], + "_callNodeInfo": CallNodeInfoImpl { + "_cache": Map {}, + "_callNodeTable": Object { + "category": Int32Array [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ], + "depth": Array [ + 0, + 1, + 2, + 2, + 3, + 2, + 3, + 2, + 3, + ], + "func": Int32Array [ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + ], + "innerWindowID": Float64Array [ + 0, + 0, + 0, + 0, + 0, + 0, + 2, + 0, + 0, + ], + "length": 9, + "maxDepth": 3, + "nextSibling": Int32Array [ + -1, + -1, + 3, + 5, + -1, + 7, + -1, + -1, + -1, + ], + "prefix": Int32Array [ + -1, + 0, + 1, + 1, + 3, + 1, + 5, + 1, + 7, + ], + "sourceFramesInlinedIntoSymbol": Array [ + null, + null, + null, + null, + null, + null, + null, + null, + null, + ], + "subcategory": Int32Array [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ], + "subtreeRangeEnd": Uint32Array [ + 9, + 9, + 3, + 5, + 5, + 7, + 7, + 9, + 9, + ], + }, + "_isInverted": false, + "_stackIndexToCallNodeIndex": Int32Array [ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + ], + }, + "_callNodeTable": Object { + "category": Int32Array [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ], + "depth": Array [ + 0, + 1, + 2, + 2, + 3, + 2, + 3, + 2, + 3, + ], + "func": Int32Array [ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + ], + "innerWindowID": Float64Array [ + 0, + 0, + 0, + 0, + 0, + 0, + 2, + 0, + 0, + ], + "length": 9, + "maxDepth": 3, + "nextSibling": Int32Array [ + -1, + -1, + 3, + 5, + -1, + 7, + -1, + -1, + -1, + ], + "prefix": Int32Array [ + -1, + 0, + 1, + 1, + 3, + 1, + 5, + 1, + 7, + ], + "sourceFramesInlinedIntoSymbol": Array [ + null, + null, + null, + null, + null, + null, + null, + null, + null, + ], + "subcategory": Int32Array [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ], + "subtreeRangeEnd": Uint32Array [ + 9, + 9, + 3, + 5, + 5, + 7, + 7, + 9, + 9, + ], + }, + "_callTreeTimings": Object { + "callNodeHasChildren": Uint8Array [ + 1, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + ], + "leaf": Float32Array [ + 0, + 0, + 0, + 0, + 0, + 0, + 2, + 0, + 0, + ], + "rootTotalSummary": 2, + "self": Float32Array [ + 0, + 0, + 0, + 0, + 0, + 0, + 2, + 0, + 0, + ], + "total": Float32Array [ + 2, + 2, + 0, + 0, + 0, + 2, + 2, + 0, + 0, + ], + }, + }, "_isHighPrecision": false, "_rootTotalSummary": 2, + "_roots": Array [ + 0, + ], "_thread": Object { "frameTable": Object { "address": Array [ From 996ae19477c572f8dcc5d50ad9f100ea00e1d9f7 Mon Sep 17 00:00:00 2001 From: "depfu[bot]" <23717796+depfu[bot]@users.noreply.github.com> Date: Wed, 24 Jan 2024 12:16:26 +0100 Subject: [PATCH 32/55] Update all Yarn dependencies (2024-01-24) (#4902) Co-authored-by: depfu[bot] <23717796+depfu[bot]@users.noreply.github.com> --- package.json | 6 +++--- yarn.lock | 34 +++++++++++++++++----------------- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/package.json b/package.json index 90bb28235b..24ac424ec3 100644 --- a/package.json +++ b/package.json @@ -85,7 +85,7 @@ "react": "^18.2.0", "react-dom": "^18.2.0", "react-intersection-observer": "^9.5.3", - "react-redux": "^9.0.4", + "react-redux": "^9.1.0", "react-splitter-layout": "^4.0.0", "react-transition-group": "^4.4.5", "redux": "^5.0.1", @@ -151,10 +151,10 @@ "mkdirp": "^3.0.1", "node-fetch": "^2.6.11", "npm-run-all": "^4.1.5", - "open": "^10.0.1", + "open": "^10.0.3", "postcss": "^8.4.33", "postcss-loader": "^7.3.4", - "prettier": "^3.1.1", + "prettier": "^3.2.4", "raw-loader": "^4.0.2", "rimraf": "^5.0.5", "style-loader": "^3.3.4", diff --git a/yarn.lock b/yarn.lock index c2bc3e974f..aea68b88a1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4400,10 +4400,10 @@ default-browser-id@^5.0.0: resolved "https://registry.yarnpkg.com/default-browser-id/-/default-browser-id-5.0.0.tgz#a1d98bf960c15082d8a3fa69e83150ccccc3af26" integrity sha512-A6p/pu/6fyBcA1TRz/GqWYPViplrftcW2gZC9q79ngNCKAeR/X3gcEdXQHl4KNXV+3wgIJ1CPkJQ3IHM6lcsyA== -default-browser@^5.2.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/default-browser/-/default-browser-5.2.0.tgz#31222b11e34165ccf7e5cb5cae872d0de173a7b6" - integrity sha512-EviRCeDkniKC30N3uKNHIaIsBZEo00ZOrxDZQmJwJeY2q6Uli5SwfmQ6wSf5aezcBS3PDHoQJl/XuAHEfMCJFg== +default-browser@^5.2.1: + version "5.2.1" + resolved "https://registry.yarnpkg.com/default-browser/-/default-browser-5.2.1.tgz#7b7ba61204ff3e425b556869ae6d3e9d9f1712cf" + integrity sha512-WY/3TUME0x3KPYdRRxEJJvXRHV4PyPoUsxtZa78lwItwRQRHhd2U9xOscaT/YTf8uCXIAjeJOFBVEh/7FtD8Xg== dependencies: bundle-name "^4.1.0" default-browser-id "^5.0.0" @@ -9614,12 +9614,12 @@ only@~0.0.2: resolved "https://registry.yarnpkg.com/only/-/only-0.0.2.tgz#2afde84d03e50b9a8edc444e30610a70295edfb4" integrity sha1-Kv3oTQPlC5qO3EROMGEKcCle37Q= -open@^10.0.1: - version "10.0.1" - resolved "https://registry.yarnpkg.com/open/-/open-10.0.1.tgz#1883d6762935c75960284b66190f12f787e30622" - integrity sha512-+BCGXjtXs4qlmImA8X1AhSU2XyBqD8tJC/PNgWYbcEvNphnw2nyCvNEhAXYk9V+55h1B8M64oXind989YcWANw== +open@^10.0.3: + version "10.0.3" + resolved "https://registry.yarnpkg.com/open/-/open-10.0.3.tgz#f60d8db49fa126c50aec751957fb5d7de3308d4f" + integrity sha512-dtbI5oW7987hwC9qjJTyABldTaa19SuyJse1QboWv3b0qCcrrLNVDqBx1XgELAjh9QTVQaP/C5b1nhQebd1H2A== dependencies: - default-browser "^5.2.0" + default-browser "^5.2.1" define-lazy-prop "^3.0.0" is-inside-container "^1.0.0" is-wsl "^3.1.0" @@ -10327,10 +10327,10 @@ prettier@^2.0.5: resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.8.tgz#e8c5d7e98a4305ffe3de2e1fc4aca1a71c28b1da" integrity sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q== -prettier@^3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.1.1.tgz#6ba9f23165d690b6cbdaa88cb0807278f7019848" - integrity sha512-22UbSzg8luF4UuZtzgiUOfcGM8s4tjBv6dJRT7j275NXsy2jb4aJa4NNveul5x4eqlF1wuhuR2RElK71RvmVaw== +prettier@^3.2.4: + version "3.2.4" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.2.4.tgz#4723cadeac2ce7c9227de758e5ff9b14e075f283" + integrity sha512-FWu1oLHKCrtpO1ypU6J0SbK2d9Ckwysq6bHj/uaCP26DxrPpppCLQRGVuqAxSTvhF00AcvDRyYrLNW7ocBhFFQ== pretty-bytes@^5.3.0, pretty-bytes@^5.4.1: version "5.6.0" @@ -10611,10 +10611,10 @@ react-is@^18.0.0: resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.1.0.tgz#61aaed3096d30eacf2a2127118b5b41387d32a67" integrity sha512-Fl7FuabXsJnV5Q1qIOQwx/sagGF18kogb4gpfcG4gjLBWO0WDiiz1ko/ExayuxE7InyQkBLkxRFG5oxY6Uu3Kg== -react-redux@^9.0.4: - version "9.0.4" - resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-9.0.4.tgz#6892d465f086507a517d4b53eb589876e6bc8344" - integrity sha512-9J1xh8sWO0vYq2sCxK2My/QO7MzUMRi3rpiILP/+tDr8krBHixC6JMM17fMK88+Oh3e4Ae6/sHIhNBgkUivwFA== +react-redux@^9.1.0: + version "9.1.0" + resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-9.1.0.tgz#46a46d4cfed4e534ce5452bb39ba18e1d98a8197" + integrity sha512-6qoDzIO+gbrza8h3hjMA9aq4nwVFCKFtY2iLxCtVT38Swyy2C/dJCGBXHeHLtx6qlg/8qzc2MrhOeduf5K32wQ== dependencies: "@types/use-sync-external-store" "^0.0.3" use-sync-external-store "^1.0.0" From 33ec4b7c02e4f9d5481ff4b1a046664c37077faa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Naz=C4=B1m=20Can=20Alt=C4=B1nova?= Date: Wed, 24 Jan 2024 17:32:41 +0100 Subject: [PATCH 33/55] Bring the max stacking depth limit back for marker timings (#4898) --- src/profile-logic/marker-timing.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/profile-logic/marker-timing.js b/src/profile-logic/marker-timing.js index 0f280838d7..8635198b75 100644 --- a/src/profile-logic/marker-timing.js +++ b/src/profile-logic/marker-timing.js @@ -10,6 +10,10 @@ import type { MarkerTimingAndBuckets, } from 'firefox-profiler/types'; +// Arbitrarily set an upper limit for adding marker depths, avoiding very long +// overlapping marker timings. +const MAX_STACKING_DEPTH = 300; + /** * This function computes the timing information for laying out the markers in the * MarkerChart component. Each marker is put into a single row based on its name. In @@ -163,6 +167,12 @@ export function getMarkerTiming( continue; } + if (markerTimingsForName.length >= MAX_STACKING_DEPTH) { + // There are too many markers stacked around the same time already, let's + // ignore this marker. + continue; + } + // Otherwise, let's add a new row! const newTiming = emptyTiming({ instantOnly: false }); addCurrentMarkerToMarkerTiming(newTiming); From 3fd468f516c25047428293d7ca8da061284aafd8 Mon Sep 17 00:00:00 2001 From: "depfu[bot]" <23717796+depfu[bot]@users.noreply.github.com> Date: Thu, 25 Jan 2024 12:11:55 +0100 Subject: [PATCH 34/55] =?UTF-8?q?=E2=AC=86=EF=B8=8F=20Update=20postcss-loa?= =?UTF-8?q?der=20to=20version=208.0.0=20(#4903)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: depfu[bot] <23717796+depfu[bot]@users.noreply.github.com> --- package.json | 2 +- yarn.lock | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/package.json b/package.json index 24ac424ec3..b410b261c9 100644 --- a/package.json +++ b/package.json @@ -153,7 +153,7 @@ "npm-run-all": "^4.1.5", "open": "^10.0.3", "postcss": "^8.4.33", - "postcss-loader": "^7.3.4", + "postcss-loader": "^8.0.0", "prettier": "^3.2.4", "raw-loader": "^4.0.2", "rimraf": "^5.0.5", diff --git a/yarn.lock b/yarn.lock index aea68b88a1..2d803f4185 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3965,7 +3965,7 @@ cosmiconfig@^7.0.0: path-type "^4.0.0" yaml "^1.10.0" -cosmiconfig@^8.2.0, cosmiconfig@^8.3.5: +cosmiconfig@^8.2.0: version "8.3.6" resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-8.3.6.tgz#060a2b871d66dba6c8538ea1118ba1ac16f5fae3" integrity sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA== @@ -10083,12 +10083,12 @@ postcss-discard-overridden@^6.0.1: resolved "https://registry.yarnpkg.com/postcss-discard-overridden/-/postcss-discard-overridden-6.0.1.tgz#c63c559237758d74bc505452393a64dda9b19ef4" integrity sha512-qs0ehZMMZpSESbRkw1+inkf51kak6OOzNRaoLd/U7Fatp0aN2HQ1rxGOrJvYcRAN9VpX8kUF13R2ofn8OlvFVA== -postcss-loader@^7.3.4: - version "7.3.4" - resolved "https://registry.yarnpkg.com/postcss-loader/-/postcss-loader-7.3.4.tgz#aed9b79ce4ed7e9e89e56199d25ad1ec8f606209" - integrity sha512-iW5WTTBSC5BfsBJ9daFMPVrLT36MrNiC6fqOZTTaHjBNX6Pfd5p+hSBqe/fEeNd7pc13QiAyGt7VdGMw4eRC4A== +postcss-loader@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/postcss-loader/-/postcss-loader-8.0.0.tgz#98bb2fb8f5b8e756ef9847e639f6e323d3f15745" + integrity sha512-+RiNlmYd1aXYv6QSBOAu6n9eJYy0ydyXTfjljAJ3vFU6MMo2M552zTVcBpBH+R5aAeKaYVG1K9UEyAVsLL1Qjg== dependencies: - cosmiconfig "^8.3.5" + cosmiconfig "^9.0.0" jiti "^1.20.0" semver "^7.5.4" From f3d791e2f80753b9cf96d37755f8aac8f910359e Mon Sep 17 00:00:00 2001 From: Markus Stange Date: Fri, 12 Jan 2024 10:31:12 -0500 Subject: [PATCH 35/55] Expose non-inverted call node table on the inverted CallNodeInfo. --- src/profile-logic/call-node-info.js | 28 +- src/profile-logic/profile-data.js | 12 +- src/selectors/per-thread/stack-sample.js | 10 +- .../__snapshots__/profile-view.test.js.snap | 342 ++++++++++++++++++ src/test/unit/address-timings.test.js | 15 +- src/test/unit/line-timings.test.js | 15 +- src/test/unit/profile-data.test.js | 13 +- src/test/unit/profile-tree.test.js | 2 + src/types/profile-derived.js | 8 + 9 files changed, 436 insertions(+), 9 deletions(-) diff --git a/src/profile-logic/call-node-info.js b/src/profile-logic/call-node-info.js index 3d85522a0a..cf28cd6747 100644 --- a/src/profile-logic/call-node-info.js +++ b/src/profile-logic/call-node-info.js @@ -21,12 +21,23 @@ export class CallNodeInfoImpl implements CallNodeInfo { // If true, call node indexes describe nodes in the inverted call tree. _isInverted: boolean; - // The call node table. + // The call node table. This is either the inverted or the non-inverted call + // node table, depending on _isInverted. _callNodeTable: CallNodeTable; - // The mapping of stack index to corresponding call node index. + // The non-inverted call node table, regardless of _isInverted. + _nonInvertedCallNodeTable: CallNodeTable; + + // The mapping of stack index to corresponding call node index. This maps to + // either the inverted or the non-inverted call node table, depending on + // _isInverted. _stackIndexToCallNodeIndex: Int32Array; + // The mapping of stack index to corresponding non-inverted call node index. + // This always maps to the non-inverted call node table, regardless of + // _isInverted. + _stackIndexToNonInvertedCallNodeIndex: Int32Array; + // This is a Map. This map speeds up // the look-up process by caching every CallNodePath we handle which avoids // looking up parents again and again. @@ -34,11 +45,16 @@ export class CallNodeInfoImpl implements CallNodeInfo { constructor( callNodeTable: CallNodeTable, + nonInvertedCallNodeTable: CallNodeTable, stackIndexToCallNodeIndex: Int32Array, + stackIndexToNonInvertedCallNodeIndex: Int32Array, isInverted: boolean ) { this._callNodeTable = callNodeTable; + this._nonInvertedCallNodeTable = nonInvertedCallNodeTable; this._stackIndexToCallNodeIndex = stackIndexToCallNodeIndex; + this._stackIndexToNonInvertedCallNodeIndex = + stackIndexToNonInvertedCallNodeIndex; this._isInverted = isInverted; } @@ -54,6 +70,14 @@ export class CallNodeInfoImpl implements CallNodeInfo { return this._stackIndexToCallNodeIndex; } + getNonInvertedCallNodeTable(): CallNodeTable { + return this._nonInvertedCallNodeTable; + } + + getStackIndexToNonInvertedCallNodeIndex(): Int32Array { + return this._stackIndexToNonInvertedCallNodeIndex; + } + getCallNodePathFromIndex( callNodeIndex: IndexIntoCallNodeTable | null ): CallNodePath { diff --git a/src/profile-logic/profile-data.js b/src/profile-logic/profile-data.js index 6a8226a40f..91aee33749 100644 --- a/src/profile-logic/profile-data.js +++ b/src/profile-logic/profile-data.js @@ -105,7 +105,13 @@ export function getCallNodeInfo( funcTable, defaultCategory ); - return new CallNodeInfoImpl(callNodeTable, stackIndexToCallNodeIndex, false); + return new CallNodeInfoImpl( + callNodeTable, + callNodeTable, + stackIndexToCallNodeIndex, + stackIndexToCallNodeIndex, + false + ); } type CallNodeTableAndStackMap = { @@ -423,6 +429,8 @@ function _createCallNodeTableFromUnorderedComponents( */ export function getInvertedCallNodeInfo( thread: Thread, + nonInvertedCallNodeTable: CallNodeTable, + stackIndexToNonInvertedCallNodeIndex: Int32Array, defaultCategory: IndexIntoCategoryList ): CallNodeInfo { // We compute an inverted stack table, but we don't let it escape this function. @@ -468,7 +476,9 @@ export function getInvertedCallNodeInfo( } return new CallNodeInfoImpl( callNodeTable, + nonInvertedCallNodeTable, nonInvertedStackIndexToCallNodeIndex, + stackIndexToNonInvertedCallNodeIndex, true ); } diff --git a/src/selectors/per-thread/stack-sample.js b/src/selectors/per-thread/stack-sample.js index d10525c03e..2f556548ab 100644 --- a/src/selectors/per-thread/stack-sample.js +++ b/src/selectors/per-thread/stack-sample.js @@ -116,8 +116,16 @@ export function getStackAndSampleSelectorsPerThread( const _getInvertedCallNodeInfo: Selector = createSelectorWithTwoCacheSlots( threadSelectors.getFilteredThread, + _getNonInvertedCallNodeInfo, ProfileSelectors.getDefaultCategory, - ProfileData.getInvertedCallNodeInfo + (thread, nonInvertedCallNodeInfo, defaultCategory) => { + return ProfileData.getInvertedCallNodeInfo( + thread, + nonInvertedCallNodeInfo.getNonInvertedCallNodeTable(), + nonInvertedCallNodeInfo.getStackIndexToNonInvertedCallNodeIndex(), + defaultCategory + ); + } ); const getCallNodeInfo: Selector = (state) => { diff --git a/src/test/store/__snapshots__/profile-view.test.js.snap b/src/test/store/__snapshots__/profile-view.test.js.snap index a2a9958e8e..e72d94552a 100644 --- a/src/test/store/__snapshots__/profile-view.test.js.snap +++ b/src/test/store/__snapshots__/profile-view.test.js.snap @@ -2288,6 +2288,109 @@ CallNodeInfoImpl { ], }, "_isInverted": false, + "_nonInvertedCallNodeTable": Object { + "category": Int32Array [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ], + "depth": Array [ + 0, + 1, + 2, + 2, + 3, + 2, + 3, + 2, + 3, + ], + "func": Int32Array [ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + ], + "innerWindowID": Float64Array [ + 0, + 0, + 0, + 0, + 0, + 0, + 2, + 0, + 0, + ], + "length": 9, + "maxDepth": 3, + "nextSibling": Int32Array [ + -1, + -1, + 3, + 5, + -1, + 7, + -1, + -1, + -1, + ], + "prefix": Int32Array [ + -1, + 0, + 1, + 1, + 3, + 1, + 5, + 1, + 7, + ], + "sourceFramesInlinedIntoSymbol": Array [ + null, + null, + null, + null, + null, + null, + null, + null, + null, + ], + "subcategory": Int32Array [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ], + "subtreeRangeEnd": Uint32Array [ + 9, + 9, + 3, + 5, + 5, + 7, + 7, + 9, + 9, + ], + }, "_stackIndexToCallNodeIndex": Int32Array [ 0, 1, @@ -2299,6 +2402,17 @@ CallNodeInfoImpl { 7, 8, ], + "_stackIndexToNonInvertedCallNodeIndex": Int32Array [ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + ], } `; @@ -2410,6 +2524,109 @@ CallTree { ], }, "_isInverted": false, + "_nonInvertedCallNodeTable": Object { + "category": Int32Array [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ], + "depth": Array [ + 0, + 1, + 2, + 2, + 3, + 2, + 3, + 2, + 3, + ], + "func": Int32Array [ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + ], + "innerWindowID": Float64Array [ + 0, + 0, + 0, + 0, + 0, + 0, + 2, + 0, + 0, + ], + "length": 9, + "maxDepth": 3, + "nextSibling": Int32Array [ + -1, + -1, + 3, + 5, + -1, + 7, + -1, + -1, + -1, + ], + "prefix": Int32Array [ + -1, + 0, + 1, + 1, + 3, + 1, + 5, + 1, + 7, + ], + "sourceFramesInlinedIntoSymbol": Array [ + null, + null, + null, + null, + null, + null, + null, + null, + null, + ], + "subcategory": Int32Array [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ], + "subtreeRangeEnd": Uint32Array [ + 9, + 9, + 3, + 5, + 5, + 7, + 7, + 9, + 9, + ], + }, "_stackIndexToCallNodeIndex": Int32Array [ 0, 1, @@ -2421,6 +2638,17 @@ CallTree { 7, 8, ], + "_stackIndexToNonInvertedCallNodeIndex": Int32Array [ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + ], }, "_callNodeTable": Object { "category": Int32Array [ @@ -2703,6 +2931,109 @@ CallTree { ], }, "_isInverted": false, + "_nonInvertedCallNodeTable": Object { + "category": Int32Array [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ], + "depth": Array [ + 0, + 1, + 2, + 2, + 3, + 2, + 3, + 2, + 3, + ], + "func": Int32Array [ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + ], + "innerWindowID": Float64Array [ + 0, + 0, + 0, + 0, + 0, + 0, + 2, + 0, + 0, + ], + "length": 9, + "maxDepth": 3, + "nextSibling": Int32Array [ + -1, + -1, + 3, + 5, + -1, + 7, + -1, + -1, + -1, + ], + "prefix": Int32Array [ + -1, + 0, + 1, + 1, + 3, + 1, + 5, + 1, + 7, + ], + "sourceFramesInlinedIntoSymbol": Array [ + null, + null, + null, + null, + null, + null, + null, + null, + null, + ], + "subcategory": Int32Array [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ], + "subtreeRangeEnd": Uint32Array [ + 9, + 9, + 3, + 5, + 5, + 7, + 7, + 9, + 9, + ], + }, "_stackIndexToCallNodeIndex": Int32Array [ 0, 1, @@ -2714,6 +3045,17 @@ CallTree { 7, 8, ], + "_stackIndexToNonInvertedCallNodeIndex": Int32Array [ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + ], }, "_callNodeTable": Object { "category": Int32Array [ diff --git a/src/test/unit/address-timings.test.js b/src/test/unit/address-timings.test.js index 101da31661..ecf594a7c6 100644 --- a/src/test/unit/address-timings.test.js +++ b/src/test/unit/address-timings.test.js @@ -158,9 +158,20 @@ describe('getAddressTimings for getStackAddressInfoForCallNode', function () { isInverted: boolean ) { const { stackTable, frameTable, funcTable, samples } = thread; + const nonInvertedCallNodeInfo = getCallNodeInfo( + stackTable, + frameTable, + funcTable, + defaultCat + ); const callNodeInfo = isInverted - ? getInvertedCallNodeInfo(thread, defaultCat) - : getCallNodeInfo(stackTable, frameTable, funcTable, defaultCat); + ? getInvertedCallNodeInfo( + thread, + nonInvertedCallNodeInfo.getNonInvertedCallNodeTable(), + nonInvertedCallNodeInfo.getStackIndexToNonInvertedCallNodeIndex(), + defaultCat + ) + : nonInvertedCallNodeInfo; const callNodeIndex = ensureExists( callNodeInfo.getCallNodeIndexFromPath(callNodePath), 'invalid call node path' diff --git a/src/test/unit/line-timings.test.js b/src/test/unit/line-timings.test.js index 63ef358c6c..aec1d1ade2 100644 --- a/src/test/unit/line-timings.test.js +++ b/src/test/unit/line-timings.test.js @@ -121,9 +121,20 @@ describe('getLineTimings for getStackLineInfoForCallNode', function () { isInverted: boolean ) { const { stackTable, frameTable, funcTable, samples } = thread; + const nonInvertedCallNodeInfo = getCallNodeInfo( + stackTable, + frameTable, + funcTable, + defaultCat + ); const callNodeInfo = isInverted - ? getInvertedCallNodeInfo(thread, defaultCat) - : getCallNodeInfo(stackTable, frameTable, funcTable, defaultCat); + ? getInvertedCallNodeInfo( + thread, + nonInvertedCallNodeInfo.getNonInvertedCallNodeTable(), + nonInvertedCallNodeInfo.getStackIndexToNonInvertedCallNodeIndex(), + defaultCat + ) + : nonInvertedCallNodeInfo; const callNodeIndex = ensureExists( callNodeInfo.getCallNodeIndexFromPath(callNodePath), 'invalid call node path' diff --git a/src/test/unit/profile-data.test.js b/src/test/unit/profile-data.test.js index 32a7f67cd9..81e39ca49f 100644 --- a/src/test/unit/profile-data.test.js +++ b/src/test/unit/profile-data.test.js @@ -1195,7 +1195,18 @@ describe('getNativeSymbolsForCallNode', function () { 'Expected to find categories' ); const defaultCategory = categories.findIndex((c) => c.name === 'Other'); - const callNodeInfo = getInvertedCallNodeInfo(thread, defaultCategory); + const nonInvertedCallNodeInfo = getCallNodeInfo( + thread.stackTable, + thread.frameTable, + thread.funcTable, + defaultCategory + ); + const callNodeInfo = getInvertedCallNodeInfo( + thread, + nonInvertedCallNodeInfo.getNonInvertedCallNodeTable(), + nonInvertedCallNodeInfo.getStackIndexToNonInvertedCallNodeIndex(), + defaultCategory + ); const c = callNodeInfo.getCallNodeIndexFromPath([funC]); expect(c).not.toBeNull(); diff --git a/src/test/unit/profile-tree.test.js b/src/test/unit/profile-tree.test.js index eb81d70663..bea414dcd1 100644 --- a/src/test/unit/profile-tree.test.js +++ b/src/test/unit/profile-tree.test.js @@ -470,6 +470,8 @@ describe('inverted call tree', function () { // Now compute the inverted tree and check it. const invertedCallNodeInfo = getInvertedCallNodeInfo( thread, + callNodeInfo.getNonInvertedCallNodeTable(), + callNodeInfo.getStackIndexToNonInvertedCallNodeIndex(), defaultCategory ); const invertedCallTreeTimings = computeCallTreeTimings( diff --git a/src/types/profile-derived.js b/src/types/profile-derived.js index 956ad7a456..5f792168bf 100644 --- a/src/types/profile-derived.js +++ b/src/types/profile-derived.js @@ -132,6 +132,10 @@ export interface CallNodeInfo { // call node table, otherwise this is the non-inverted call node table. getCallNodeTable(): CallNodeTable; + // Returns the non-inverted call node table. + // This is always the non-inverted call node table, regardless of isInverted(). + getNonInvertedCallNodeTable(): CallNodeTable; + // Returns a mapping from the stack table to the call node table. // The Int32Array should be used as if it were a // Map. @@ -150,6 +154,10 @@ export interface CallNodeInfo { // C <- B <- A in the inverted call node table. getStackIndexToCallNodeIndex(): Int32Array; + // Returns a mapping from the stack table to the non-inverted call node table. + // This always maps to the non-inverted call node table, regardless of isInverted(). + getStackIndexToNonInvertedCallNodeIndex(): Int32Array; + // Converts a call node index into a call node path. getCallNodePathFromIndex( callNodeIndex: IndexIntoCallNodeTable | null From d43bde929d1a8d0ee7f1afc7586c64e5dfe40fea Mon Sep 17 00:00:00 2001 From: Markus Stange Date: Sun, 22 Oct 2023 20:03:05 -0400 Subject: [PATCH 36/55] Use getNonInvertedCallNodeTable in flame graph code. Flame graph code is always executed with a non-inverted CallNodeInfo, so getNonInvertedCallNodeTable always returns the same as getCallNodeTable. The purpose of this patch is to remove more callers of getCallNodeTable() without changing behavior. --- src/components/flame-graph/Canvas.js | 4 ++-- src/components/flame-graph/FlameGraph.js | 8 ++++---- src/selectors/per-thread/stack-sample.js | 4 ++-- src/test/store/actions.test.js | 4 ++-- src/test/unit/profile-tree.test.js | 2 +- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/components/flame-graph/Canvas.js b/src/components/flame-graph/Canvas.js index d730d71313..61d9176d6b 100644 --- a/src/components/flame-graph/Canvas.js +++ b/src/components/flame-graph/Canvas.js @@ -163,7 +163,7 @@ class FlameGraphCanvasImpl extends React.PureComponent { return; } - const callNodeTable = callNodeInfo.getCallNodeTable(); + const callNodeTable = callNodeInfo.getNonInvertedCallNodeTable(); const depth = callNodeTable.depth[selectedCallNodeIndex]; const y = (maxStackDepthPlusOne - depth - 1) * ROW_HEIGHT; @@ -237,7 +237,7 @@ class FlameGraphCanvasImpl extends React.PureComponent { fastFillStyle.set('#ffffff'); ctx.fillRect(0, 0, deviceContainerWidth, deviceContainerHeight); - const callNodeTable = callNodeInfo.getCallNodeTable(); + const callNodeTable = callNodeInfo.getNonInvertedCallNodeTable(); const startDepth = Math.floor( maxStackDepthPlusOne - viewportBottom / stackFrameHeight diff --git a/src/components/flame-graph/FlameGraph.js b/src/components/flame-graph/FlameGraph.js index f80531e8fd..2d221359a9 100644 --- a/src/components/flame-graph/FlameGraph.js +++ b/src/components/flame-graph/FlameGraph.js @@ -163,7 +163,7 @@ class FlameGraphImpl extends React.PureComponent { _wideEnough = (callNodeIndex: IndexIntoCallNodeTable): boolean => { const { flameGraphTiming, callNodeInfo } = this.props; - const callNodeTable = callNodeInfo.getCallNodeTable(); + const callNodeTable = callNodeInfo.getNonInvertedCallNodeTable(); const depth = callNodeTable.depth[callNodeIndex]; const row = flameGraphTiming[depth]; const columnIndex = row.callNode.indexOf(callNodeIndex); @@ -188,7 +188,7 @@ class FlameGraphImpl extends React.PureComponent { let callNodeIndex = startingCallNodeIndex; - const callNodeTable = callNodeInfo.getCallNodeTable(); + const callNodeTable = callNodeInfo.getNonInvertedCallNodeTable(); const depth = callNodeTable.depth[callNodeIndex]; const row = flameGraphTiming[depth]; let columnIndex = row.callNode.indexOf(callNodeIndex); @@ -219,7 +219,7 @@ class FlameGraphImpl extends React.PureComponent { changeSelectedCallNode, handleCallNodeTransformShortcut, } = this.props; - const callNodeTable = callNodeInfo.getCallNodeTable(); + const callNodeTable = callNodeInfo.getNonInvertedCallNodeTable(); if ( // Please do not forget to update the switch/case below if changing the array to allow more keys. @@ -305,7 +305,7 @@ class FlameGraphImpl extends React.PureComponent { if (document.activeElement === this._viewport) { event.preventDefault(); const { callNodeInfo, selectedCallNodeIndex, thread } = this.props; - const callNodeTable = callNodeInfo.getCallNodeTable(); + const callNodeTable = callNodeInfo.getNonInvertedCallNodeTable(); if (selectedCallNodeIndex !== null) { const funcIndex = callNodeTable.func[selectedCallNodeIndex]; const funcName = thread.stringTable.getString( diff --git a/src/selectors/per-thread/stack-sample.js b/src/selectors/per-thread/stack-sample.js index 2f556548ab..e896ff8751 100644 --- a/src/selectors/per-thread/stack-sample.js +++ b/src/selectors/per-thread/stack-sample.js @@ -405,7 +405,7 @@ export function getStackAndSampleSelectorsPerThread( ); const getFlameGraphRows: Selector = createSelector( - (state) => getCallNodeInfo(state).getCallNodeTable(), + (state) => getCallNodeInfo(state).getNonInvertedCallNodeTable(), (state) => threadSelectors.getFilteredThread(state).funcTable, (state) => threadSelectors.getFilteredThread(state).stringTable, FlameGraph.computeFlameGraphRows @@ -414,7 +414,7 @@ export function getStackAndSampleSelectorsPerThread( const getFlameGraphTiming: Selector = createSelector( getFlameGraphRows, - (state) => getCallNodeInfo(state).getCallNodeTable(), + (state) => getCallNodeInfo(state).getNonInvertedCallNodeTable(), getCallTreeTimings, FlameGraph.getFlameGraphTiming ); diff --git a/src/test/store/actions.test.js b/src/test/store/actions.test.js index cddcf82e47..3e8fe7d1eb 100644 --- a/src/test/store/actions.test.js +++ b/src/test/store/actions.test.js @@ -149,7 +149,7 @@ describe('selectors/getFlameGraphTiming', function () { const callNodeInfo = selectedThreadSelectors.getCallNodeInfo( store.getState() ); - const callNodeTable = callNodeInfo.getCallNodeTable(); + const callNodeTable = callNodeInfo.getNonInvertedCallNodeTable(); const flameGraphTiming = selectedThreadSelectors.getFlameGraphTiming( store.getState() ); @@ -180,7 +180,7 @@ describe('selectors/getFlameGraphTiming', function () { const callNodeInfo = selectedThreadSelectors.getCallNodeInfo( store.getState() ); - const callNodeTable = callNodeInfo.getCallNodeTable(); + const callNodeTable = callNodeInfo.getNonInvertedCallNodeTable(); const flameGraphTiming = selectedThreadSelectors.getFlameGraphTiming( store.getState() ); diff --git a/src/test/unit/profile-tree.test.js b/src/test/unit/profile-tree.test.js index bea414dcd1..4ba030b5d8 100644 --- a/src/test/unit/profile-tree.test.js +++ b/src/test/unit/profile-tree.test.js @@ -127,7 +127,7 @@ describe('unfiltered call tree', function () { const cnKN = callNodeInfo.getCallNodeIndexFromPath([K, N]); const rows = computeFlameGraphRows( - callNodeInfo.getCallNodeTable(), + callNodeInfo.getNonInvertedCallNodeTable(), thread.funcTable, thread.stringTable ); From 7a5abecc26d350ae7a9167230129b988a136574c Mon Sep 17 00:00:00 2001 From: Markus Stange Date: Sun, 22 Oct 2023 21:02:49 -0400 Subject: [PATCH 37/55] Use non-inverted call node table for ThreadStackGraph. --- src/components/shared/thread/StackGraph.js | 21 +++++++++++---------- src/components/timeline/TrackThread.js | 12 +++++++----- src/selectors/per-thread/stack-sample.js | 13 +++++++++++++ 3 files changed, 31 insertions(+), 15 deletions(-) diff --git a/src/components/shared/thread/StackGraph.js b/src/components/shared/thread/StackGraph.js index b605584edd..f56efd68f7 100644 --- a/src/components/shared/thread/StackGraph.js +++ b/src/components/shared/thread/StackGraph.js @@ -21,7 +21,7 @@ type Props = {| +className: string, +thread: Thread, +samplesSelectedStates: null | SelectedState[], - +sampleCallNodes: Array, + +sampleNonInvertedCallNodes: Array, +interval: Milliseconds, +rangeStart: Milliseconds, +rangeEnd: Milliseconds, @@ -38,13 +38,14 @@ type Props = {| export class ThreadStackGraph extends PureComponent { _heightFunction = (sampleIndex: IndexIntoSamplesTable): number | null => { - const { callNodeInfo, sampleCallNodes } = this.props; - const callNodeIndex = sampleCallNodes[sampleIndex]; - if (callNodeIndex === null) { + const { callNodeInfo, sampleNonInvertedCallNodes } = this.props; + const nonInvertedCallNodeIndex = sampleNonInvertedCallNodes[sampleIndex]; + if (nonInvertedCallNodeIndex === null) { return null; } - const callNodeTable = callNodeInfo.getCallNodeTable(); - return callNodeTable.depth[callNodeIndex]; + + const nonInvertedCallNodeTable = callNodeInfo.getNonInvertedCallNodeTable(); + return nonInvertedCallNodeTable.depth[nonInvertedCallNodeIndex]; }; render() { @@ -60,12 +61,12 @@ export class ThreadStackGraph extends PureComponent { trackName, onSampleClick, } = this.props; - const callNodeTable = callNodeInfo.getCallNodeTable(); + const nonInvertedCallNodeTable = callNodeInfo.getNonInvertedCallNodeTable(); let maxDepth = 0; - for (let i = 0; i < callNodeTable.depth.length; i++) { - if (callNodeTable.depth[i] > maxDepth) { - maxDepth = callNodeTable.depth[i]; + for (let i = 0; i < nonInvertedCallNodeTable.depth.length; i++) { + if (nonInvertedCallNodeTable.depth[i] > maxDepth) { + maxDepth = nonInvertedCallNodeTable.depth[i]; } } diff --git a/src/components/timeline/TrackThread.js b/src/components/timeline/TrackThread.js index 9ed1cdc09c..e4ee81e9eb 100644 --- a/src/components/timeline/TrackThread.js +++ b/src/components/timeline/TrackThread.js @@ -85,7 +85,7 @@ type StateProps = {| +timelineType: TimelineType, +hasFileIoMarkers: boolean, +samplesSelectedStates: null | SelectedState[], - +sampleCallNodes: Array, + +sampleNonInvertedCallNodes: Array, +invertCallstack: boolean, +treeOrderSampleComparator: ( IndexIntoSamplesTable, @@ -198,7 +198,7 @@ class TimelineTrackThreadImpl extends PureComponent { timelineType, hasFileIoMarkers, showMemoryMarkers, - sampleCallNodes, + sampleNonInvertedCallNodes, samplesSelectedStates, treeOrderSampleComparator, trackType, @@ -317,7 +317,7 @@ class TimelineTrackThreadImpl extends PureComponent { rangeStart={rangeStart} rangeEnd={rangeEnd} callNodeInfo={callNodeInfo} - sampleCallNodes={sampleCallNodes} + sampleNonInvertedCallNodes={sampleNonInvertedCallNodes} samplesSelectedStates={samplesSelectedStates} categories={categories} onSampleClick={this._onSampleClick} @@ -357,8 +357,10 @@ export const TimelineTrackThread = explicitConnect< filteredThread: selectors.getFilteredThread(state), rangeFilteredThread: selectors.getRangeFilteredThread(state), callNodeInfo: selectors.getCallNodeInfo(state), - sampleCallNodes: - selectors.getSampleIndexToCallNodeIndexForFilteredThread(state), + sampleNonInvertedCallNodes: + selectors.getSampleIndexToNonInvertedCallNodeIndexForFilteredThread( + state + ), unfilteredSamplesRange: selectors.unfilteredSamplesRange(state), interval: getProfileInterval(state), rangeStart: committedRange.start, diff --git a/src/selectors/per-thread/stack-sample.js b/src/selectors/per-thread/stack-sample.js index e896ff8751..bd897ea1ad 100644 --- a/src/selectors/per-thread/stack-sample.js +++ b/src/selectors/per-thread/stack-sample.js @@ -251,6 +251,18 @@ export function getStackAndSampleSelectorsPerThread( ProfileData.getSampleIndexToCallNodeIndex ); + const getSampleIndexToNonInvertedCallNodeIndexForFilteredThread: Selector< + Array, + > = createSelector( + (state) => threadSelectors.getFilteredThread(state).samples.stack, + (state) => getCallNodeInfo(state).getStackIndexToNonInvertedCallNodeIndex(), + (filteredThreadSampleStacks, stackIndexToNonInvertedCallNodeIndex) => + ProfileData.getSampleIndexToCallNodeIndex( + filteredThreadSampleStacks, + stackIndexToNonInvertedCallNodeIndex + ) + ); + const getSampleIndexToCallNodeIndexForTabFilteredThread: Selector< Array, > = createSelector( @@ -449,6 +461,7 @@ export function getStackAndSampleSelectorsPerThread( getExpandedCallNodePaths, getExpandedCallNodeIndexes, getSampleIndexToCallNodeIndexForFilteredThread, + getSampleIndexToNonInvertedCallNodeIndexForFilteredThread, getSampleIndexToCallNodeIndexForTabFilteredThread, getSamplesSelectedStatesInFilteredThread, getTreeOrderComparatorInFilteredThread, From 64a28a76c0a1f9ccd07d37eec718c2993c8ebb26 Mon Sep 17 00:00:00 2001 From: Markus Stange Date: Fri, 12 Jan 2024 16:01:42 -0500 Subject: [PATCH 38/55] Use getNonInvertedCallNodeTable in some test code. --- src/test/unit/profile-data.test.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/test/unit/profile-data.test.js b/src/test/unit/profile-data.test.js index 81e39ca49f..5eff9f333f 100644 --- a/src/test/unit/profile-data.test.js +++ b/src/test/unit/profile-data.test.js @@ -450,7 +450,7 @@ describe('profile-data', function () { thread.funcTable, defaultCategory ); - const callNodeTable = callNodeInfo.getCallNodeTable(); + const callNodeTable = callNodeInfo.getNonInvertedCallNodeTable(); it('should create one callNode per original stack', function () { // After nudgeReturnAddresses, the stack table now has 8 entries. @@ -505,9 +505,9 @@ describe('profile-data', function () { thread.funcTable, defaultCategory ); - const callNodeTable = callNodeInfo.getCallNodeTable(); + const callNodeTable = callNodeInfo.getNonInvertedCallNodeTable(); const stackIndexToCallNodeIndex = - callNodeInfo.getStackIndexToCallNodeIndex(); + callNodeInfo.getStackIndexToNonInvertedCallNodeIndex(); const stack0 = thread.samples.stack[0]; const stack1 = thread.samples.stack[1]; if (stack0 === null || stack1 === null) { @@ -874,7 +874,8 @@ describe('getSamplesSelectedStates', function () { thread.funcTable, 0 ); - const stackIndexToCallNodeIndex = callNodeInfo.getStackIndexToCallNodeIndex(); + const stackIndexToCallNodeIndex = + callNodeInfo.getStackIndexToNonInvertedCallNodeIndex(); const sampleCallNodes = getSampleIndexToCallNodeIndex( thread.samples.stack, stackIndexToCallNodeIndex From ac38706ea6a03230cb2563b96b97e09202032c52 Mon Sep 17 00:00:00 2001 From: Markus Stange Date: Mon, 13 Nov 2023 12:14:01 -0500 Subject: [PATCH 39/55] Use getStackIndexToNonInvertedCallNodeIndex in functions where we're guaranteed to have a non-inverted call node info. In these three functions we've already checked whether the CallNodeInfo is inverted. We only get here if it's the non-inverted CallNodeInfo. So getStackIndexToNonInvertedCallNodeIndex will return the same as getStackIndexToCallNodeIndex. --- src/profile-logic/address-timings.js | 3 ++- src/profile-logic/line-timings.js | 3 ++- src/profile-logic/profile-data.js | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/profile-logic/address-timings.js b/src/profile-logic/address-timings.js index 3ff041777b..00080a3f3d 100644 --- a/src/profile-logic/address-timings.js +++ b/src/profile-logic/address-timings.js @@ -350,7 +350,8 @@ export function getStackAddressInfoForCallNodeNonInverted( callNodeInfo: CallNodeInfo, nativeSymbol: IndexIntoNativeSymbolTable ): StackAddressInfo { - const stackIndexToCallNodeIndex = callNodeInfo.getStackIndexToCallNodeIndex(); + const stackIndexToCallNodeIndex = + callNodeInfo.getStackIndexToNonInvertedCallNodeIndex(); // "self address" == "the address which a stack's self time is contributed to" const callNodeSelfAddressForAllStacks = []; diff --git a/src/profile-logic/line-timings.js b/src/profile-logic/line-timings.js index 91ab961117..0d0065f25c 100644 --- a/src/profile-logic/line-timings.js +++ b/src/profile-logic/line-timings.js @@ -209,7 +209,8 @@ export function getStackLineInfoForCallNodeNonInverted( callNodeIndex: IndexIntoCallNodeTable, callNodeInfo: CallNodeInfo ): StackLineInfo { - const stackIndexToCallNodeIndex = callNodeInfo.getStackIndexToCallNodeIndex(); + const stackIndexToCallNodeIndex = + callNodeInfo.getStackIndexToNonInvertedCallNodeIndex(); // "self line" == "the line which a stack's self time is contributed to" const callNodeSelfLineForAllStacks = []; diff --git a/src/profile-logic/profile-data.js b/src/profile-logic/profile-data.js index 91aee33749..b723fa63b2 100644 --- a/src/profile-logic/profile-data.js +++ b/src/profile-logic/profile-data.js @@ -3558,7 +3558,8 @@ export function getNativeSymbolsForCallNodeNonInverted( stackTable: StackTable, frameTable: FrameTable ): IndexIntoNativeSymbolTable[] { - const stackIndexToCallNodeIndex = callNodeInfo.getStackIndexToCallNodeIndex(); + const stackIndexToCallNodeIndex = + callNodeInfo.getStackIndexToNonInvertedCallNodeIndex(); const set = new Set(); for (let stackIndex = 0; stackIndex < stackTable.length; stackIndex++) { if (stackIndexToCallNodeIndex[stackIndex] === callNodeIndex) { From 8b227be4be93c9fa9283ee22133198f55f56f728 Mon Sep 17 00:00:00 2001 From: Markus Stange Date: Mon, 13 Nov 2023 11:24:52 -0500 Subject: [PATCH 40/55] Use getNonInvertedCallNodeTable in computeCallNodeMaxDepthPlusOne. --- src/profile-logic/profile-data.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/profile-logic/profile-data.js b/src/profile-logic/profile-data.js index b723fa63b2..a8e08a5920 100644 --- a/src/profile-logic/profile-data.js +++ b/src/profile-logic/profile-data.js @@ -2021,8 +2021,10 @@ export function computeCallNodeMaxDepthPlusOne( // computed for the filtered thread, but a samples-like table can use the preview // filtered thread, which involves a subset of the total call nodes. let maxDepth = -1; - const callNodeTable = callNodeInfo.getCallNodeTable(); - const stackIndexToCallNodeIndex = callNodeInfo.getStackIndexToCallNodeIndex(); + const callNodeTable = callNodeInfo.getNonInvertedCallNodeTable(); + // TODO: Use sampleCallNodes instead + const stackIndexToCallNodeIndex = + callNodeInfo.getStackIndexToNonInvertedCallNodeIndex(); for (let sampleIndex = 0; sampleIndex < samples.length; sampleIndex++) { const stackIndex = samples.stack[sampleIndex]; if (stackIndex === null) { From f1b2b144b940cd00eaf4e46c68db925ebdaae9d2 Mon Sep 17 00:00:00 2001 From: Markus Stange Date: Sun, 12 Nov 2023 20:49:58 -0500 Subject: [PATCH 41/55] Remove unnecessary isInvertedTree argument. --- src/components/flame-graph/Canvas.js | 2 -- src/profile-logic/profile-data.js | 4 +--- src/selectors/per-thread/index.js | 1 - 3 files changed, 1 insertion(+), 6 deletions(-) diff --git a/src/components/flame-graph/Canvas.js b/src/components/flame-graph/Canvas.js index 61d9176d6b..b89490658e 100644 --- a/src/components/flame-graph/Canvas.js +++ b/src/components/flame-graph/Canvas.js @@ -367,7 +367,6 @@ class FlameGraphCanvasImpl extends React.PureComponent { shouldDisplayTooltips, categories, interval, - isInverted, callTreeSummaryStrategy, innerWindowIDToPageMap, weightType, @@ -426,7 +425,6 @@ class FlameGraphCanvasImpl extends React.PureComponent { callNodeIndex, callNodeInfo, interval, - isInverted, thread, unfilteredThread, sampleIndexOffset, diff --git a/src/profile-logic/profile-data.js b/src/profile-logic/profile-data.js index a8e08a5920..42c287c43c 100644 --- a/src/profile-logic/profile-data.js +++ b/src/profile-logic/profile-data.js @@ -786,7 +786,6 @@ export function getTimingsForPath( needlePath: CallNodePath, callNodeInfo: CallNodeInfo, interval: Milliseconds, - isInvertedTree: boolean, thread: Thread, unfilteredThread: Thread, sampleIndexOffset: number, @@ -799,7 +798,6 @@ export function getTimingsForPath( callNodeInfo.getCallNodeIndexFromPath(needlePath), callNodeInfo, interval, - isInvertedTree, thread, unfilteredThread, sampleIndexOffset, @@ -822,7 +820,6 @@ export function getTimingsForCallNodeIndex( needleNodeIndex: IndexIntoCallNodeTable | null, callNodeInfo: CallNodeInfo, interval: Milliseconds, - isInvertedTree: boolean, thread: Thread, unfilteredThread: Thread, sampleIndexOffset: number, @@ -1019,6 +1016,7 @@ export function getTimingsForCallNodeIndex( const needleDescendantsEndIndex = callNodeTable.subtreeRangeEnd[needleNodeIndex]; + const isInvertedTree = callNodeInfo.isInverted(); const needleNodeIsRootOfInvertedTree = isInvertedTree && callNodeTable.prefix[needleNodeIndex] === -1; diff --git a/src/selectors/per-thread/index.js b/src/selectors/per-thread/index.js index fa734d9565..91ce053c85 100644 --- a/src/selectors/per-thread/index.js +++ b/src/selectors/per-thread/index.js @@ -263,7 +263,6 @@ export const selectedNodeSelectors: NodeSelectors = (() => { selectedThreadSelectors.getSelectedCallNodePath, selectedThreadSelectors.getCallNodeInfo, ProfileSelectors.getProfileInterval, - UrlState.getInvertCallstack, selectedThreadSelectors.getPreviewFilteredThread, selectedThreadSelectors.getThread, selectedThreadSelectors.getSampleIndexOffsetFromPreviewRange, From 806dd22e01b58c2f8d8ee54b0a597b82ce430ec3 Mon Sep 17 00:00:00 2001 From: Markus Stange Date: Mon, 13 Nov 2023 12:14:54 -0500 Subject: [PATCH 42/55] Use the non-inverted call node table when selecting samples. This replaces selectLeafCallNode and selectRootCallNode with a new action creater called selectSelfCallNode which handles both the inverted and the non-inverted case. --- src/actions/profile-view.js | 89 +++++++++++--------------- src/components/timeline/TrackThread.js | 24 ++----- 2 files changed, 43 insertions(+), 70 deletions(-) diff --git a/src/actions/profile-view.js b/src/actions/profile-view.js index 76e9c32098..65ad672115 100644 --- a/src/actions/profile-view.js +++ b/src/actions/profile-view.js @@ -148,77 +148,64 @@ export function changeRightClickedCallNode( } /** - * Given a threadIndex and a sampleIndex, select the call node at the top ("leaf") - * of that sample's stack. + * Given a threadIndex and a sampleIndex, select the call node which carries the + * sample's self time. In the inverted tree, this will be a root node. */ -export function selectLeafCallNode( +export function selectSelfCallNode( threadsKey: ThreadsKey, sampleIndex: IndexIntoSamplesTable | null ): ThunkAction { return (dispatch, getState) => { const threadSelectors = getThreadSelectorsFromThreadsKey(threadsKey); const sampleCallNodes = - threadSelectors.getSampleIndexToCallNodeIndexForFilteredThread( + threadSelectors.getSampleIndexToNonInvertedCallNodeIndexForFilteredThread( getState() ); - const callNodeInfo = threadSelectors.getCallNodeInfo(getState()); - let newSelectedCallNode = -1; if ( - sampleIndex !== null && - sampleIndex >= 0 && - sampleIndex < sampleCallNodes.length + sampleIndex === null || + sampleIndex < 0 || + sampleIndex >= sampleCallNodes.length ) { - newSelectedCallNode = sampleCallNodes[sampleIndex] ?? -1; - } - - dispatch( - changeSelectedCallNode( - threadsKey, - callNodeInfo.getCallNodePathFromIndex(newSelectedCallNode) - ) - ); - }; -} - -/** - * Given a threadIndex and a sampleIndex, select the call node at the bottom ("root") - * of that sample's stack. - */ -export function selectRootCallNode( - threadsKey: ThreadsKey, - sampleIndex: IndexIntoSamplesTable | null -): ThunkAction { - return (dispatch, getState) => { - const threadSelectors = getThreadSelectorsFromThreadsKey(threadsKey); - const filteredThread = threadSelectors.getFilteredThread(getState()); - const callNodeInfo = threadSelectors.getCallNodeInfo(getState()); - - if (sampleIndex === null) { dispatch(changeSelectedCallNode(threadsKey, [])); return; } - const newSelectedStack = filteredThread.samples.stack[sampleIndex]; - if (newSelectedStack === null || newSelectedStack === undefined) { + + const nonInvertedSelfCallNode = sampleCallNodes[sampleIndex]; + if (nonInvertedSelfCallNode === null) { dispatch(changeSelectedCallNode(threadsKey, [])); return; } - const stackIndexToCallNodeIndex = - callNodeInfo.getStackIndexToCallNodeIndex(); - const newSelectedCallNode = stackIndexToCallNodeIndex[newSelectedStack]; - const selectedCallNodePath = - callNodeInfo.getCallNodePathFromIndex(newSelectedCallNode); - const rootCallNodePath = [selectedCallNodePath[0]]; + const callNodeInfo = threadSelectors.getCallNodeInfo(getState()); - dispatch( - changeSelectedCallNode( - threadsKey, - rootCallNodePath, - { source: 'auto' }, - selectedCallNodePath - ) - ); + // Compute the call path based on the non-inverted call node table. + // We're not calling callNodeInfo.getCallNodePathFromIndex here because we + // only have a non-inverted call node index, which wouldn't be accepted by + // the inverted call node info. + const callNodeTable = callNodeInfo.getNonInvertedCallNodeTable(); + const callNodePath = []; + let cni = nonInvertedSelfCallNode; + while (cni !== -1) { + callNodePath.push(callNodeTable.func[cni]); + cni = callNodeTable.prefix[cni]; + } + + if (callNodeInfo.isInverted()) { + // In the inverted tree, we want to select the inverted tree root node + // with the "self" function, and also expand the path to the non-inverted root. + dispatch( + changeSelectedCallNode( + threadsKey, + callNodePath.slice(0, 1), // Select a root node + { source: 'auto' }, + callNodePath // Expand the full path + ) + ); + } else { + // In the non-inverted tree, we want to select the self node. + dispatch(changeSelectedCallNode(threadsKey, callNodePath.reverse())); + } }; } diff --git a/src/components/timeline/TrackThread.js b/src/components/timeline/TrackThread.js index e4ee81e9eb..0ad7c5f392 100644 --- a/src/components/timeline/TrackThread.js +++ b/src/components/timeline/TrackThread.js @@ -22,7 +22,6 @@ import { getCategories, getSelectedThreadIndexes, getTimelineType, - getInvertCallstack, getThreadSelectorsFromThreadsKey, getMaxThreadCPUDeltaPerMs, getIsExperimentalCPUGraphsEnabled, @@ -38,8 +37,7 @@ import { updatePreviewSelection, changeSelectedCallNode, focusCallTree, - selectLeafCallNode, - selectRootCallNode, + selectSelfCallNode, } from 'firefox-profiler/actions/profile-view'; import { reportTrackThreadHeight } from 'firefox-profiler/actions/app'; import { EmptyThreadIndicator } from './EmptyThreadIndicator'; @@ -86,7 +84,6 @@ type StateProps = {| +hasFileIoMarkers: boolean, +samplesSelectedStates: null | SelectedState[], +sampleNonInvertedCallNodes: Array, - +invertCallstack: boolean, +treeOrderSampleComparator: ( IndexIntoSamplesTable, IndexIntoSamplesTable @@ -103,8 +100,7 @@ type DispatchProps = {| +updatePreviewSelection: typeof updatePreviewSelection, +changeSelectedCallNode: typeof changeSelectedCallNode, +focusCallTree: typeof focusCallTree, - +selectLeafCallNode: typeof selectLeafCallNode, - +selectRootCallNode: typeof selectRootCallNode, + +selectSelfCallNode: typeof selectSelfCallNode, +reportTrackThreadHeight: typeof reportTrackThreadHeight, |}; @@ -130,23 +126,15 @@ class TimelineTrackThreadImpl extends PureComponent { const { threadsKey, - selectLeafCallNode, - selectRootCallNode, + selectSelfCallNode, focusCallTree, - invertCallstack, selectedThreadIndexes, callTreeVisible, } = this.props; // Sample clicking only works for one thread. See issue #2709 if (selectedThreadIndexes.size === 1) { - if (invertCallstack) { - // When we're displaying the inverted call stack, the "leaf" call node we're - // interested in is actually displayed as the "root" of the tree. - selectRootCallNode(threadsKey, sampleIndex); - } else { - selectLeafCallNode(threadsKey, sampleIndex); - } + selectSelfCallNode(threadsKey, sampleIndex); if (sampleIndex !== null && callTreeVisible) { // If the user clicked outside of the activity graph (sampleIndex === null), @@ -352,7 +340,6 @@ export const TimelineTrackThread = explicitConnect< fullThread.samples.threadCPUDelta !== undefined; return { - invertCallstack: getInvertCallstack(state), fullThread, filteredThread: selectors.getFilteredThread(state), rangeFilteredThread: selectors.getRangeFilteredThread(state), @@ -387,8 +374,7 @@ export const TimelineTrackThread = explicitConnect< updatePreviewSelection, changeSelectedCallNode, focusCallTree, - selectLeafCallNode, - selectRootCallNode, + selectSelfCallNode, reportTrackThreadHeight, }, component: withSize(TimelineTrackThreadImpl), From 70a1ccfcd88197bfa74c51385e040ad96a35154e Mon Sep 17 00:00:00 2001 From: Artem Polivanchuk Date: Sun, 28 Jan 2024 18:24:08 +0000 Subject: [PATCH 43/55] Pontoon: Update Ukrainian (uk) localization of Firefox Profiler Co-authored-by: Artem Polivanchuk --- locales/uk/app.ftl | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/locales/uk/app.ftl b/locales/uk/app.ftl index c9e18aa59a..b777f29ad5 100644 --- a/locales/uk/app.ftl +++ b/locales/uk/app.ftl @@ -836,6 +836,46 @@ TrackPower--tooltip-energy-carbon-used-in-preview-milliwatthour = { $value } м TrackPower--tooltip-energy-carbon-used-in-preview-microwatthour = { $value } мкВт·год ({ $carbonValue } мг CO₂e) .label = Спожита у поточній вибірці енергія +## TrackBandwidth +## This is used to show how much data was transfered over time. +## For the strings in this group, the carbon dioxide equivalent is estimated +## from the amount of data transfered. +## The carbon dioxide equivalent represents the equivalent amount +## of CO₂ to achieve the same level of global warming potential. + +# This is used in the tooltip of the bandwidth track. +# Variables: +# $value (String) - the value for the data transfer speed. +# Will contain the unit (eg. B, KB, MB) +TrackBandwidthGraph--speed = { $value } за секунду + .label = Швидкість передавання для цього зразка +# This is used in the tooltip of the bandwidth track. +# Variables: +# $value (String) - how many read or write operations were performed since the previous sample +TrackBandwidthGraph--read-write-operations-since-the-previous-sample = { $value } + .label = операцій читання/запису від попереднього зразка +# This is used in the tooltip of the bandwidth track. +# Variables: +# $value (String) - the total of transfered data until the hovered time. +# Will contain the unit (eg. B, KB, MB) +# $carbonValue (string) - the carbon dioxide equivalent (CO₂e) value in grams +TrackBandwidthGraph--cumulative-bandwidth-at-this-time = { $value } ({ $carbonValue } г CO₂e) + .label = Дані, передані до цього часу +# This is used in the tooltip of the bandwidth track. +# Variables: +# $value (String) - the total of transfered data during the visible time range. +# Will contain the unit (eg. B, KB, MB) +# $carbonValue (string) - the carbon dioxide equivalent (CO₂e) value in grams +TrackBandwidthGraph--total-bandwidth-in-graph = { $value } ({ $carbonValue } г CO₂e) + .label = Дані, передані у видимому діапазоні +# This is used in the tooltip of the bandwidth track when a range is selected. +# Variables: +# $value (String) - the total of transfered data during the selected time range. +# Will contain the unit (eg. B, KB, MB) +# $carbonValue (string) - the carbon dioxide equivalent (CO₂e) value in grams +TrackBandwidthGraph--total-bandwidth-in-range = { $value } ({ $carbonValue } г CO₂e) + .label = Дані, передані в поточному виборі + ## TrackSearchField ## The component that is used for the search input in the track context menu. From b2e0414ca1c4b6872feb6277412fe074ff5e6529 Mon Sep 17 00:00:00 2001 From: Melo46 Date: Tue, 30 Jan 2024 09:05:16 +0000 Subject: [PATCH 44/55] Pontoon: Update Interlingua (ia) localization of Firefox Profiler Co-authored-by: Melo46 --- locales/ia/app.ftl | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/locales/ia/app.ftl b/locales/ia/app.ftl index 055a3d6759..61943d1c91 100644 --- a/locales/ia/app.ftl +++ b/locales/ia/app.ftl @@ -824,6 +824,28 @@ TrackPower--tooltip-energy-carbon-used-in-preview-milliwatthour = { $value } mWh TrackPower--tooltip-energy-carbon-used-in-preview-microwatthour = { $value } µWh ({ $carbonValue } mg CO₂e) .label = Energia usate in le selection actual +## TrackBandwidth +## This is used to show how much data was transfered over time. +## For the strings in this group, the carbon dioxide equivalent is estimated +## from the amount of data transfered. +## The carbon dioxide equivalent represents the equivalent amount +## of CO₂ to achieve the same level of global warming potential. + +# This is used in the tooltip of the bandwidth track. +# Variables: +# $value (String) - the total of transfered data during the visible time range. +# Will contain the unit (eg. B, KB, MB) +# $carbonValue (string) - the carbon dioxide equivalent (CO₂e) value in grams +TrackBandwidthGraph--total-bandwidth-in-graph = { $value } ({ $carbonValue } g CO₂e) + .label = Datos transferite in le campo visibile +# This is used in the tooltip of the bandwidth track when a range is selected. +# Variables: +# $value (String) - the total of transfered data during the selected time range. +# Will contain the unit (eg. B, KB, MB) +# $carbonValue (string) - the carbon dioxide equivalent (CO₂e) value in grams +TrackBandwidthGraph--total-bandwidth-in-range = { $value } ({ $carbonValue } g CO₂e) + .label = Datos transferite in le selection actual + ## TrackSearchField ## The component that is used for the search input in the track context menu. From e29cbe2d9061228e6437e562364cfa7aa00c1df3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20Qu=C3=A8ze?= Date: Wed, 31 Jan 2024 14:01:19 +0100 Subject: [PATCH 45/55] Show marker count next to the marker name in the marker chart when a marker is hovered. (#4892) --- src/components/marker-chart/Canvas.js | 42 +++++++++++++++---- .../__snapshots__/MarkerChart.test.js.snap | 18 ++++++-- 2 files changed, 50 insertions(+), 10 deletions(-) diff --git a/src/components/marker-chart/Canvas.js b/src/components/marker-chart/Canvas.js index 74c62871c5..3fae84d869 100644 --- a/src/components/marker-chart/Canvas.js +++ b/src/components/marker-chart/Canvas.js @@ -207,7 +207,7 @@ class MarkerChartCanvasImpl extends React.PureComponent { this.highlightRow(ctx, newRow); this.drawMarkers(ctx, hoveredMarker, newRow, newRow + 1); if (hoveredLabel === null) { - this.drawSeparatorsAndLabels(ctx, newRow, newRow + 1); + this.drawSeparatorsAndLabels(ctx, newRow, newRow + 1, true); } } if (oldRow !== undefined && oldRow !== newRow) { @@ -542,10 +542,32 @@ class MarkerChartCanvasImpl extends React.PureComponent { return this._textMeasurement; } + countMarkersInBucketStartingAtRow(rowIndex: number): number { + const { markerTimingAndBuckets } = this.props; + const markerTiming = markerTimingAndBuckets[rowIndex]; + if (typeof markerTiming === 'string') { + return 0; + } + + const { name } = markerTiming; + let count = markerTiming.length; + for (let row = rowIndex + 1; row < markerTimingAndBuckets.length; ++row) { + if ( + typeof markerTimingAndBuckets[row] === 'string' || + markerTimingAndBuckets[row].name !== name + ) { + break; + } + count += markerTimingAndBuckets[row].length; + } + return count; + } + drawSeparatorsAndLabels( ctx: CanvasRenderingContext2D, startRow: number, - endRow: number + endRow: number, + drawMarkerCount: boolean = false ) { const { markerTimingAndBuckets, @@ -587,11 +609,17 @@ class MarkerChartCanvasImpl extends React.PureComponent { } const y = rowIndex * rowHeight - viewportTop; - // Even though it's on active tab view, have a hard cap on the text length. - const fittedText = textMeasurement.getFittedText( - name, - TIMELINE_MARGIN_LEFT - ); + + const countString = drawMarkerCount + ? ` (${this.countMarkersInBucketStartingAtRow(rowIndex)})` + : ''; + // Even when it's on active tab view, have a hard cap on the text length. + const fittedText = + textMeasurement.getFittedText( + name, + TIMELINE_MARGIN_LEFT - + (countString ? textMeasurement.getTextWidth(countString) : 0) + ) + countString; if (timelineTrackOrganization.type === 'active-tab') { // Draw the text backgound for active tab. diff --git a/src/test/components/__snapshots__/MarkerChart.test.js.snap b/src/test/components/__snapshots__/MarkerChart.test.js.snap index 33bfed573f..f12b9498fc 100644 --- a/src/test/components/__snapshots__/MarkerChart.test.js.snap +++ b/src/test/components/__snapshots__/MarkerChart.test.js.snap @@ -261,9 +261,13 @@ Array [ "set fillStyle", "#000000", ], + Array [ + "measureText", + " (1)", + ], Array [ "fillText", - "UserTiming", + "UserTiming (1)", 5, 139, ], @@ -1262,15 +1266,23 @@ Array [ "set fillStyle", "#000000", ], + Array [ + "measureText", + " (1)", + ], Array [ "set fillStyle", "#ffffffbf", ], + Array [ + "measureText", + "Marker DomEvent (1)", + ], Array [ "fillRect", 0, 16, - 85, + 105, 16, ], Array [ @@ -1279,7 +1291,7 @@ Array [ ], Array [ "fillText", - "Marker DomEvent", + "Marker DomEvent (1)", 5, 27, ], From e399e83bd6e6a704ecbf48263829c82418d577ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20Qu=C3=A8ze?= Date: Wed, 31 Jan 2024 14:08:12 +0100 Subject: [PATCH 46/55] Improve the precision of the getFittedText implementation. (#4893) --- src/components/marker-chart/Canvas.js | 1 + .../__snapshots__/MarkerChart.test.js.snap | 8 ++++ .../__snapshots__/StackChart.test.js.snap | 8 ++++ src/utils/text-measurement.js | 38 +++++++++++++++---- 4 files changed, 48 insertions(+), 7 deletions(-) diff --git a/src/components/marker-chart/Canvas.js b/src/components/marker-chart/Canvas.js index 3fae84d869..be5b68d6aa 100644 --- a/src/components/marker-chart/Canvas.js +++ b/src/components/marker-chart/Canvas.js @@ -618,6 +618,7 @@ class MarkerChartCanvasImpl extends React.PureComponent { textMeasurement.getFittedText( name, TIMELINE_MARGIN_LEFT - + LABEL_PADDING - (countString ? textMeasurement.getTextWidth(countString) : 0) ) + countString; diff --git a/src/test/components/__snapshots__/MarkerChart.test.js.snap b/src/test/components/__snapshots__/MarkerChart.test.js.snap index f12b9498fc..39dee0fe33 100644 --- a/src/test/components/__snapshots__/MarkerChart.test.js.snap +++ b/src/test/components/__snapshots__/MarkerChart.test.js.snap @@ -1009,6 +1009,10 @@ Array [ "measureText", "Non-interval marker F without data", ], + Array [ + "measureText", + "Non-interval marker F withou", + ], Array [ "fillText", "Non-interval marker F withou…", @@ -1029,6 +1033,10 @@ Array [ "measureText", "Very very very very very very Very very very very very very Very very very very very very Very very very very very very Very very very very very very long Marker D", ], + Array [ + "measureText", + "Very very very very very ver", + ], Array [ "fillText", "Very very very very very ver…", diff --git a/src/test/components/__snapshots__/StackChart.test.js.snap b/src/test/components/__snapshots__/StackChart.test.js.snap index d73e7e7b9d..48d711e4a1 100644 --- a/src/test/components/__snapshots__/StackChart.test.js.snap +++ b/src/test/components/__snapshots__/StackChart.test.js.snap @@ -754,6 +754,14 @@ Array [ "measureText", "componentD", ], + Array [ + "measureText", + "c", + ], + Array [ + "measureText", + "co", + ], Array [ "set fillStyle", "#000000", diff --git a/src/utils/text-measurement.js b/src/utils/text-measurement.js index 27e8c04371..6a48f04ff8 100644 --- a/src/utils/text-measurement.js +++ b/src/utils/text-measurement.js @@ -65,7 +65,7 @@ class TextMeasurement { * Massage a text to fit inside a given width. This clamps the string * at the end to avoid overflowing. * - * @param {string} text -The text to fit inside the given width. + * @param {string} text - The text to fit inside the given width. * @param {number} maxWidth - The available width for the given text. * @return {string} The fitted text. */ @@ -74,16 +74,40 @@ class TextMeasurement { return text; } + // Returns the actual width of a string composed of the n first characters + // of the text variable. + const getWidth = (n) => this.getTextWidth(text.substring(0, n)); + // Estimate how many characters can still be added after taking into account + // the space used by the n first characters. The result can be negative. + const getRemainingCharacterCount = (n) => + Math.round((availableWidth - getWidth(n)) / this._averageCharWidth); + // Approximate the number of characters to truncate to, // using avg character width as reference. - const f = (maxWidth - this.minWidth) / this._averageCharWidth; + const availableWidth = maxWidth - this.minWidth; + const f = availableWidth / this._averageCharWidth; let n = Math.floor(f); - if (n === f) { - // The approximate width of `n` characters is exactly max width, - // so take one character less just in case. - n -= 1; + if (n < 1) { + return ''; + } + + // Do a second finer grained approximation to add or remove a few characters. + n += getRemainingCharacterCount(n); + + // And a third one that can only add characters. This will be useful when + // the characters added at the previous step were narrow (eg. '::'). + const offset = getRemainingCharacterCount(n); + if (offset >= 1) { + n += offset; } - return n > 0 ? text.substring(0, n) + this.overflowChar : ''; + + // If we overflow a little bit, remove characters one at a time until we no + // longer do. + while (getWidth(n) > availableWidth) { + --n; + } + + return text.substring(0, n) + this.overflowChar; } } From 6229247661e691f61edaaf6d4846619af080232e Mon Sep 17 00:00:00 2001 From: Melo46 Date: Wed, 31 Jan 2024 23:05:43 +0000 Subject: [PATCH 47/55] Pontoon: Update Interlingua (ia) localization of Firefox Profiler Co-authored-by: Melo46 --- locales/ia/app.ftl | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/locales/ia/app.ftl b/locales/ia/app.ftl index 61943d1c91..8897959a4b 100644 --- a/locales/ia/app.ftl +++ b/locales/ia/app.ftl @@ -831,6 +831,24 @@ TrackPower--tooltip-energy-carbon-used-in-preview-microwatthour = { $value } µW ## The carbon dioxide equivalent represents the equivalent amount ## of CO₂ to achieve the same level of global warming potential. +# This is used in the tooltip of the bandwidth track. +# Variables: +# $value (String) - the value for the data transfer speed. +# Will contain the unit (eg. B, KB, MB) +TrackBandwidthGraph--speed = { $value } per secunda + .label = Velocitate de transferentia pro iste specimen +# This is used in the tooltip of the bandwidth track. +# Variables: +# $value (String) - how many read or write operations were performed since the previous sample +TrackBandwidthGraph--read-write-operations-since-the-previous-sample = { $value } + .label = operationes de lectura/scriptura depost le specimen previe +# This is used in the tooltip of the bandwidth track. +# Variables: +# $value (String) - the total of transfered data until the hovered time. +# Will contain the unit (eg. B, KB, MB) +# $carbonValue (string) - the carbon dioxide equivalent (CO₂e) value in grams +TrackBandwidthGraph--cumulative-bandwidth-at-this-time = { $value } ({ $carbonValue } g CO₂e) + .label = Datos transferite usque ora # This is used in the tooltip of the bandwidth track. # Variables: # $value (String) - the total of transfered data during the visible time range. From 0372f784587473b9699b612177fd2615a4619b0d Mon Sep 17 00:00:00 2001 From: Olvcpr423 Date: Thu, 1 Feb 2024 17:33:17 +0000 Subject: [PATCH 48/55] Pontoon: Update Chinese (China) (zh-CN) localization of Firefox Profiler Co-authored-by: Olvcpr423 --- locales/zh-CN/app.ftl | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/locales/zh-CN/app.ftl b/locales/zh-CN/app.ftl index 0dcba03a4a..d92f4e4cbd 100644 --- a/locales/zh-CN/app.ftl +++ b/locales/zh-CN/app.ftl @@ -780,6 +780,13 @@ TrackBandwidthGraph--cumulative-bandwidth-at-this-time = { $value }({ $carbonV # $carbonValue (string) - the carbon dioxide equivalent (CO₂e) value in grams TrackBandwidthGraph--total-bandwidth-in-graph = { $value }({ $carbonValue } g CO₂e) .label = 可见范围内传输的数据 +# This is used in the tooltip of the bandwidth track when a range is selected. +# Variables: +# $value (String) - the total of transfered data during the selected time range. +# Will contain the unit (eg. B, KB, MB) +# $carbonValue (string) - the carbon dioxide equivalent (CO₂e) value in grams +TrackBandwidthGraph--total-bandwidth-in-range = { $value }({ $carbonValue } g CO₂e) + .label = 当前选中部分传输的数据 ## TrackSearchField ## The component that is used for the search input in the track context menu. From e19f08d0e13245cb44c26a0f52983a9a32b5e68d Mon Sep 17 00:00:00 2001 From: "depfu[bot]" <23717796+depfu[bot]@users.noreply.github.com> Date: Fri, 2 Feb 2024 10:59:31 +0100 Subject: [PATCH 49/55] Update all Yarn dependencies (2024-01-31) (#4907) Co-authored-by: depfu[bot] <23717796+depfu[bot]@users.noreply.github.com> --- package.json | 14 ++-- yarn.lock | 176 +++++++++++++++++++++++++-------------------------- 2 files changed, 95 insertions(+), 95 deletions(-) diff --git a/package.json b/package.json index b410b261c9..b3edd083d0 100644 --- a/package.json +++ b/package.json @@ -58,7 +58,7 @@ "@codemirror/lang-rust": "^6.0.1", "@codemirror/language": "^6.10.0", "@codemirror/state": "^6.4.0", - "@codemirror/view": "^6.22.3", + "@codemirror/view": "^6.23.1", "@firefox-devtools/react-contextmenu": "^5.1.1", "@fluent/bundle": "^0.18.0", "@fluent/langneg": "^0.7.0", @@ -71,7 +71,7 @@ "classnames": "^2.5.1", "common-tags": "^1.8.2", "copy-to-clipboard": "^3.3.3", - "core-js": "^3.35.0", + "core-js": "^3.35.1", "escape-string-regexp": "^4.0.0", "gecko-profiler-demangle": "^0.3.3", "idb": "^8.0.0", @@ -109,12 +109,12 @@ "@testing-library/jest-dom": "^6.1.6", "@testing-library/react": "^14.1.2", "alex": "^11.0.1", - "autoprefixer": "^10.4.16", + "autoprefixer": "^10.4.17", "babel-jest": "^29.7.0", "babel-loader": "^9.1.3", "babel-plugin-module-resolver": "^5.0.0", "browserslist": "^4.22.2", - "caniuse-lite": "^1.0.30001572", + "caniuse-lite": "^1.0.30001579", "circular-dependency-plugin": "^5.2.1", "codecov": "^3.8.3", "copy-webpack-plugin": "^12.0.2", @@ -127,7 +127,7 @@ "eslint-import-resolver-alias": "^1.1.2", "eslint-plugin-flowtype": "^8.0.3", "eslint-plugin-import": "^2.29.1", - "eslint-plugin-jest": "^27.6.1", + "eslint-plugin-jest": "^27.6.3", "eslint-plugin-jest-dom": "^5.1.0", "eslint-plugin-jest-formatting": "^3.1.0", "eslint-plugin-react": "^7.33.2", @@ -158,10 +158,10 @@ "raw-loader": "^4.0.2", "rimraf": "^5.0.5", "style-loader": "^3.3.4", - "stylelint": "^16.1.0", + "stylelint": "^16.2.0", "stylelint-config-idiomatic-order": "^10.0.0", "stylelint-config-standard": "^36.0.0", - "webpack": "^5.89.0", + "webpack": "^5.90.0", "webpack-cli": "^5.1.4", "webpack-dev-server": "^4.15.1", "workbox-webpack-plugin": "^7.0.0", diff --git a/yarn.lock b/yarn.lock index 2d803f4185..2ab5b94a9b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1170,26 +1170,26 @@ resolved "https://registry.yarnpkg.com/@codemirror/state/-/state-6.4.0.tgz#8bc3e096c84360b34525a84696a84f86b305363a" integrity sha512-hm8XshYj5Fo30Bb922QX9hXB/bxOAVH+qaqHBzw5TKa72vOeslyGwd4X8M0c1dJ9JqxlaMceOQ8RsL9tC7gU0A== -"@codemirror/view@^6.0.0", "@codemirror/view@^6.17.0", "@codemirror/view@^6.22.3", "@codemirror/view@^6.23.0": - version "6.23.0" - resolved "https://registry.yarnpkg.com/@codemirror/view/-/view-6.23.0.tgz#8054a2043273abad7f1587d15accb0623e1960ed" - integrity sha512-/51px9N4uW8NpuWkyUX+iam5+PM6io2fm+QmRnzwqBy5v/pwGg9T0kILFtYeum8hjuvENtgsGNKluOfqIICmeQ== +"@codemirror/view@^6.0.0", "@codemirror/view@^6.17.0", "@codemirror/view@^6.23.0", "@codemirror/view@^6.23.1": + version "6.23.1" + resolved "https://registry.yarnpkg.com/@codemirror/view/-/view-6.23.1.tgz#1ce3039a588d6b93f153b7c4c035c2075ede34a6" + integrity sha512-J2Xnn5lFYT1ZN/5ewEoMBCmLlL71lZ3mBdb7cUEuHhX2ESoSrNEucpsDXpX22EuTGm9LOgC9v4Z0wx+Ez8QmGA== dependencies: "@codemirror/state" "^6.4.0" style-mod "^4.1.0" w3c-keyname "^2.2.4" -"@csstools/css-parser-algorithms@^2.4.0": +"@csstools/css-parser-algorithms@^2.5.0": version "2.5.0" resolved "https://registry.yarnpkg.com/@csstools/css-parser-algorithms/-/css-parser-algorithms-2.5.0.tgz#0c03cd5418a9f404a05ff2ffcb1b69d04e8ec532" integrity sha512-abypo6m9re3clXA00eu5syw+oaPHbJTPapu9C4pzNsJ4hdZDzushT50Zhu+iIYXgEe1CxnRMn7ngsbV+MLrlpQ== -"@csstools/css-tokenizer@^2.2.2": +"@csstools/css-tokenizer@^2.2.3": version "2.2.3" resolved "https://registry.yarnpkg.com/@csstools/css-tokenizer/-/css-tokenizer-2.2.3.tgz#b099d543ea57b64f495915a095ead583866c50c6" integrity sha512-pp//EvZ9dUmGuGtG1p+n17gTHEOqu9jO+FiCUjNN3BDmyhdA2Jq9QsVeR7K8/2QCK17HSsioPlTW9ZkzoWb3Lg== -"@csstools/media-query-list-parser@^2.1.6": +"@csstools/media-query-list-parser@^2.1.7": version "2.1.7" resolved "https://registry.yarnpkg.com/@csstools/media-query-list-parser/-/media-query-list-parser-2.1.7.tgz#a4836e3dbd693081a30b32ce9c2a781e1be16788" integrity sha512-lHPKJDkPUECsyAvD60joYfDmp8UERYxHGkFfyLJFTVK/ERJe0sVlIFLXU5XFxdjNDTerp5L4KeaKG+Z5S94qxQ== @@ -1522,36 +1522,36 @@ "@jridgewell/sourcemap-codec" "^1.4.10" "@jridgewell/trace-mapping" "^0.3.9" -"@jridgewell/resolve-uri@3.1.0": - version "3.1.0" - resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz#2203b118c157721addfe69d47b70465463066d78" - integrity sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w== +"@jridgewell/resolve-uri@^3.1.0": + version "3.1.1" + resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz#c08679063f279615a3326583ba3a90d1d82cc721" + integrity sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA== "@jridgewell/set-array@^1.0.0", "@jridgewell/set-array@^1.0.1": version "1.1.2" resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72" integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw== -"@jridgewell/source-map@^0.3.2": - version "0.3.2" - resolved "https://registry.yarnpkg.com/@jridgewell/source-map/-/source-map-0.3.2.tgz#f45351aaed4527a298512ec72f81040c998580fb" - integrity sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw== +"@jridgewell/source-map@^0.3.3": + version "0.3.5" + resolved "https://registry.yarnpkg.com/@jridgewell/source-map/-/source-map-0.3.5.tgz#a3bb4d5c6825aab0d281268f47f6ad5853431e91" + integrity sha512-UTYAUj/wviwdsMfzoSJspJxbkH5o1snzwX0//0ENX1u/55kkZZkcTZP6u9bwKGkv+dkk9at4m1Cpt0uY80kcpQ== dependencies: "@jridgewell/gen-mapping" "^0.3.0" "@jridgewell/trace-mapping" "^0.3.9" -"@jridgewell/sourcemap-codec@1.4.14", "@jridgewell/sourcemap-codec@^1.4.10": - version "1.4.14" - resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24" - integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw== +"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14": + version "1.4.15" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32" + integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg== -"@jridgewell/trace-mapping@^0.3.12", "@jridgewell/trace-mapping@^0.3.17", "@jridgewell/trace-mapping@^0.3.18", "@jridgewell/trace-mapping@^0.3.9": - version "0.3.18" - resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz#25783b2086daf6ff1dcb53c9249ae480e4dd4cd6" - integrity sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA== +"@jridgewell/trace-mapping@^0.3.12", "@jridgewell/trace-mapping@^0.3.17", "@jridgewell/trace-mapping@^0.3.18", "@jridgewell/trace-mapping@^0.3.20", "@jridgewell/trace-mapping@^0.3.9": + version "0.3.22" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.22.tgz#72a621e5de59f5f1ef792d0793a82ee20f645e4c" + integrity sha512-Wf963MzWtA2sjrNt+g18IAln9lKnlRp+K2eH4jjIoF1wYeq3aMREpG09xhlhdzS0EjwU7qmUJYangWa+151vZw== dependencies: - "@jridgewell/resolve-uri" "3.1.0" - "@jridgewell/sourcemap-codec" "1.4.14" + "@jridgewell/resolve-uri" "^3.1.0" + "@jridgewell/sourcemap-codec" "^1.4.14" "@koa/cors@^5.0.0": version "5.0.0" @@ -2081,10 +2081,10 @@ dependencies: "@types/estree" "*" -"@types/estree@*", "@types/estree@^1.0.0": - version "1.0.0" - resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.0.tgz#5fb2e536c1ae9bf35366eed879e827fa59ca41c2" - integrity sha512-WulqXMDUTYAXCjZnk6JtIHPigp55cVtDgDrO2gHRwhyJto21+1zbVCtOYB2L1F9w4qCQ0rOGWBnBe0FNTiEJIQ== +"@types/estree@*", "@types/estree@^1.0.5": + version "1.0.5" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.5.tgz#a6ce3e556e00fd9895dd872dd172ad0d4bd687f4" + integrity sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw== "@types/estree@0.0.39": version "0.0.39" @@ -2642,10 +2642,10 @@ acorn@^7.1.1: resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.0.tgz#e1ad486e6c54501634c6c397c5c121daa383607c" integrity sha512-+G7P8jJmCHr+S+cLfQxygbWhXy+8YTVGzAkpEbcLo2mLoL7tij/VG41QSHACSf5QgYRhMZYHuNc6drJaO0Da+w== -acorn@^8.0.0, acorn@^8.5.0, acorn@^8.7.1, acorn@^8.9.0: - version "8.10.0" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.10.0.tgz#8be5b3907a67221a81ab23c7889c4c5526b62ec5" - integrity sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw== +acorn@^8.0.0, acorn@^8.7.1, acorn@^8.8.2, acorn@^8.9.0: + version "8.11.3" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.11.3.tgz#71e0b14e13a4ec160724b38fb7b0f233b1b81d7a" + integrity sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg== agent-base@6: version "6.0.1" @@ -3018,14 +3018,14 @@ at-least-node@^1.0.0: resolved "https://registry.yarnpkg.com/at-least-node/-/at-least-node-1.0.0.tgz#602cd4b46e844ad4effc92a8011a3c46e0238dc2" integrity sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg== -autoprefixer@^10.4.16: - version "10.4.16" - resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-10.4.16.tgz#fad1411024d8670880bdece3970aa72e3572feb8" - integrity sha512-7vd3UC6xKp0HLfua5IjZlcXvGAGy7cBAXTg2lyQ/8WpNhd6SiZ8Be+xm3FyBSYJx5GKcpRCzBh7RH4/0dnY+uQ== +autoprefixer@^10.4.17: + version "10.4.17" + resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-10.4.17.tgz#35cd5695cbbe82f536a50fa025d561b01fdec8be" + integrity sha512-/cpVNRLSfhOtcGflT13P2794gVSgmPgTR+erw5ifnMLZb0UnSlkK4tquLmkd3BhA+nLo5tX8Cu0upUsGKvKbmg== dependencies: - browserslist "^4.21.10" - caniuse-lite "^1.0.30001538" - fraction.js "^4.3.6" + browserslist "^4.22.2" + caniuse-lite "^1.0.30001578" + fraction.js "^4.3.7" normalize-range "^0.1.2" picocolors "^1.0.0" postcss-value-parser "^4.2.0" @@ -3271,7 +3271,7 @@ browser-process-hrtime@^1.0.0: resolved "https://registry.yarnpkg.com/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz#3c9b4b7d782c8121e56f10106d84c0d0ffc94626" integrity sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow== -browserslist@^4.0.0, browserslist@^4.14.5, browserslist@^4.21.10, browserslist@^4.22.1, browserslist@^4.22.2: +browserslist@^4.0.0, browserslist@^4.21.10, browserslist@^4.22.1, browserslist@^4.22.2: version "4.22.2" resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.22.2.tgz#704c4943072bd81ea18997f3bd2180e89c77874b" integrity sha512-0UgcrvQmBDvZHFGdYUehrCNIazki7/lUP3kkoi/r3YB2amZbFM9J43ZRkJTXBUZK4gmx56+Sqk9+Vs9mwZx9+A== @@ -3443,10 +3443,10 @@ caniuse-api@^3.0.0: lodash.memoize "^4.1.2" lodash.uniq "^4.5.0" -caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001538, caniuse-lite@^1.0.30001565, caniuse-lite@^1.0.30001572: - version "1.0.30001572" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001572.tgz#1ccf7dc92d2ee2f92ed3a54e11b7b4a3041acfa0" - integrity sha512-1Pbh5FLmn5y4+QhNyJE9j3/7dK44dGB83/ZMjv/qJk86TvDbjk0LosiZo0i0WB0Vx607qMX9jYrn1VLHCkN4rw== +caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001565, caniuse-lite@^1.0.30001578, caniuse-lite@^1.0.30001579: + version "1.0.30001581" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001581.tgz#0dfd4db9e94edbdca67d57348ebc070dece279f4" + integrity sha512-whlTkwhqV2tUmP3oYhtNfaWGYHDdS3JYFQBKXxcUR9qqPWsRhFHhoISO2Xnl/g0xyKzht9mI1LZpiNWfMzHixQ== ccount@^2.0.0: version "2.0.1" @@ -3944,10 +3944,10 @@ core-js-compat@^3.31.0, core-js-compat@^3.33.1: dependencies: browserslist "^4.22.1" -core-js@^3.0.0, core-js@^3.35.0: - version "3.35.0" - resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.35.0.tgz#58e651688484f83c34196ca13f099574ee53d6b4" - integrity sha512-ntakECeqg81KqMueeGJ79Q5ZgQNR+6eaE8sxGCx62zMbAIj65q+uYvatToew3m6eAGdU4gNZwpZ34NMe4GYswg== +core-js@^3.0.0, core-js@^3.35.1: + version "3.35.1" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.35.1.tgz#9c28f8b7ccee482796f8590cc8d15739eaaf980c" + integrity sha512-IgdsbxNyMskrTFxa9lWHyMwAJU5gXOPP+1yO+K59d50VLVAIDAbs7gIv705KzALModfK3ZrSZTPNpC0PQgIZuw== core-util-is@~1.0.0: version "1.0.3" @@ -5007,10 +5007,10 @@ eslint-plugin-jest-formatting@^3.1.0: resolved "https://registry.yarnpkg.com/eslint-plugin-jest-formatting/-/eslint-plugin-jest-formatting-3.1.0.tgz#b26dd5a40f432b642dcc880021a771bb1c93dcd2" integrity sha512-XyysraZ1JSgGbLSDxjj5HzKKh0glgWf+7CkqxbTqb7zEhW7X2WHo5SBQ8cGhnszKN+2Lj3/oevBlHNbHezoc/A== -eslint-plugin-jest@^27.6.1: - version "27.6.1" - resolved "https://registry.yarnpkg.com/eslint-plugin-jest/-/eslint-plugin-jest-27.6.1.tgz#5e43b07f3ca48d72e4b4fa243531e5153d9ca1dc" - integrity sha512-WEYkyVXD9NlmFBKvrkmzrC+C9yZoz5pAml2hO19PlS3spJtoiwj4p2u8spd/7zx5IvRsZsCmsoImaAvBB9X93Q== +eslint-plugin-jest@^27.6.3: + version "27.6.3" + resolved "https://registry.yarnpkg.com/eslint-plugin-jest/-/eslint-plugin-jest-27.6.3.tgz#8acb8b1e45597fe1f4d4cf25163d90119efc12be" + integrity sha512-+YsJFVH6R+tOiO3gCJon5oqn4KWc+mDq2leudk8mrp8RFubLOo9CVyi3cib4L7XMpxExmkmBZQTPDYVBzgpgOA== dependencies: "@typescript-eslint/utils" "^5.10.0" @@ -5751,10 +5751,10 @@ forwarded@0.2.0: resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811" integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow== -fraction.js@^4.3.6: - version "4.3.6" - resolved "https://registry.yarnpkg.com/fraction.js/-/fraction.js-4.3.6.tgz#e9e3acec6c9a28cf7bc36cbe35eea4ceb2c5c92d" - integrity sha512-n2aZ9tNfYDwaHhvFTkhFErqOMIb8uyzSQ+vGJBjZyanAKZVbGUQ1sngfk9FdkBw7G26O7AgNjLcecLffD1c7eg== +fraction.js@^4.3.7: + version "4.3.7" + resolved "https://registry.yarnpkg.com/fraction.js/-/fraction.js-4.3.7.tgz#06ca0085157e42fda7f9e726e79fefc4068840f7" + integrity sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew== fresh@0.5.2, fresh@~0.5.2: version "0.5.2" @@ -8677,7 +8677,7 @@ meow@^11.0.0: type-fest "^3.1.0" yargs-parser "^21.1.1" -meow@^13.0.0: +meow@^13.1.0: version "13.1.0" resolved "https://registry.yarnpkg.com/meow/-/meow-13.1.0.tgz#62995b0e8c3951739fe6e0a4becdd4d0df23eb37" integrity sha512-o5R/R3Tzxq0PJ3v3qcQJtSvSE9nKOLSAaDuuoMzDVuGTwHdccMWcYomh9Xolng2tjT6O/Y83d+0coVGof6tqmA== @@ -10265,7 +10265,7 @@ postcss-safe-parser@^7.0.0: resolved "https://registry.yarnpkg.com/postcss-safe-parser/-/postcss-safe-parser-7.0.0.tgz#6273d4e5149e286db5a45bc6cf6eafcad464014a" integrity sha512-ovehqRNVCpuFzbXoTb4qLtyzK3xn3t/CUBxOs8LsnQjQrShaB4lKiHoVqY8ANaC0hBMHq5QVWk77rwGklFUDrg== -postcss-selector-parser@^6.0.11, postcss-selector-parser@^6.0.13, postcss-selector-parser@^6.0.15, postcss-selector-parser@^6.0.2, postcss-selector-parser@^6.0.4: +postcss-selector-parser@^6.0.11, postcss-selector-parser@^6.0.15, postcss-selector-parser@^6.0.2, postcss-selector-parser@^6.0.4: version "6.0.15" resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.15.tgz#11cc2b21eebc0b99ea374ffb9887174855a01535" integrity sha512-rEYkQOMUCEMhsKbK66tbEU9QVIxbhN18YiniAwA7XQYTVBqrBy+P2p5JcdqsHgKM2zWylp8d7J6eszocfds5Sw== @@ -12036,14 +12036,14 @@ stylelint-order@^6.0.2: postcss "^8.4.32" postcss-sorting "^8.0.2" -stylelint@^16.1.0: - version "16.1.0" - resolved "https://registry.yarnpkg.com/stylelint/-/stylelint-16.1.0.tgz#d289c36b0dd344a65c55897d636b3b8b213dc908" - integrity sha512-Sh1rRV0lN1qxz/QsuuooLWsIZ/ona7NKw/fRZd6y6PyXYdD2W0EAzJ8yJcwSx4Iw/muz0CF09VZ+z4EiTAcKmg== +stylelint@^16.2.0: + version "16.2.0" + resolved "https://registry.yarnpkg.com/stylelint/-/stylelint-16.2.0.tgz#60678f64d7660350fdd06415fd449f332b4fcbf6" + integrity sha512-gwqU5AkIb52wrAzzn+359S3NIJDMl02TXLUaV2tzA/L6jUdpTwNt+MCxHlc8+Hb2bUHlYVo92YeSIryF2gJthA== dependencies: - "@csstools/css-parser-algorithms" "^2.4.0" - "@csstools/css-tokenizer" "^2.2.2" - "@csstools/media-query-list-parser" "^2.1.6" + "@csstools/css-parser-algorithms" "^2.5.0" + "@csstools/css-tokenizer" "^2.2.3" + "@csstools/media-query-list-parser" "^2.1.7" "@csstools/selector-specificity" "^3.0.1" balanced-match "^2.0.0" colord "^2.9.3" @@ -12063,14 +12063,14 @@ stylelint@^16.1.0: is-plain-object "^5.0.0" known-css-properties "^0.29.0" mathml-tag-names "^2.1.3" - meow "^13.0.0" + meow "^13.1.0" micromatch "^4.0.5" normalize-path "^3.0.0" picocolors "^1.0.0" - postcss "^8.4.32" + postcss "^8.4.33" postcss-resolve-nested-selector "^0.1.1" postcss-safe-parser "^7.0.0" - postcss-selector-parser "^6.0.13" + postcss-selector-parser "^6.0.15" postcss-value-parser "^4.2.0" resolve-from "^5.0.0" string-width "^4.2.3" @@ -12245,24 +12245,24 @@ terminal-table@^0.0.12: colors "^1.0.3" eastasianwidth "^0.1.0" -terser-webpack-plugin@^5.3.7: - version "5.3.7" - resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-5.3.7.tgz#ef760632d24991760f339fe9290deb936ad1ffc7" - integrity sha512-AfKwIktyP7Cu50xNjXF/6Qb5lBNzYaWpU6YfoX3uZicTx0zTy0stDDCsvjDapKsSDvOeWo5MEq4TmdBy2cNoHw== +terser-webpack-plugin@^5.3.10: + version "5.3.10" + resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-5.3.10.tgz#904f4c9193c6fd2a03f693a2150c62a92f40d199" + integrity sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w== dependencies: - "@jridgewell/trace-mapping" "^0.3.17" + "@jridgewell/trace-mapping" "^0.3.20" jest-worker "^27.4.5" schema-utils "^3.1.1" serialize-javascript "^6.0.1" - terser "^5.16.5" + terser "^5.26.0" -terser@^5.0.0, terser@^5.10.0, terser@^5.16.5: - version "5.16.9" - resolved "https://registry.yarnpkg.com/terser/-/terser-5.16.9.tgz#7a28cb178e330c484369886f2afd623d9847495f" - integrity sha512-HPa/FdTB9XGI2H1/keLFZHxl6WNvAI4YalHGtDQTlMnJcoqSab1UwL4l1hGEhs6/GmLHBZIg/YgB++jcbzoOEg== +terser@^5.0.0, terser@^5.10.0, terser@^5.26.0: + version "5.27.0" + resolved "https://registry.yarnpkg.com/terser/-/terser-5.27.0.tgz#70108689d9ab25fef61c4e93e808e9fd092bf20c" + integrity sha512-bi1HRwVRskAjheeYl291n3JC4GgO/Ty4z1nVs5AAsmonJulGxpSektecnNedrwK9C7vpvVtcX3cw00VSLt7U2A== dependencies: - "@jridgewell/source-map" "^0.3.2" - acorn "^8.5.0" + "@jridgewell/source-map" "^0.3.3" + acorn "^8.8.2" commander "^2.20.0" source-map-support "~0.5.20" @@ -13207,19 +13207,19 @@ webpack-sources@^3.2.3: resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-3.2.3.tgz#2d4daab8451fd4b240cc27055ff6a0c2ccea0cde" integrity sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w== -webpack@^5.89.0: - version "5.89.0" - resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.89.0.tgz#56b8bf9a34356e93a6625770006490bf3a7f32dc" - integrity sha512-qyfIC10pOr70V+jkmud8tMfajraGCZMBWJtrmuBymQKCrLTRejBI8STDp1MCyZu/QTdZSeacCQYpYNQVOzX5kw== +webpack@^5.90.0: + version "5.90.0" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.90.0.tgz#313bfe16080d8b2fee6e29b6c986c0714ad4290e" + integrity sha512-bdmyXRCXeeNIePv6R6tGPyy20aUobw4Zy8r0LUS2EWO+U+Ke/gYDgsCh7bl5rB6jPpr4r0SZa6dPxBxLooDT3w== dependencies: "@types/eslint-scope" "^3.7.3" - "@types/estree" "^1.0.0" + "@types/estree" "^1.0.5" "@webassemblyjs/ast" "^1.11.5" "@webassemblyjs/wasm-edit" "^1.11.5" "@webassemblyjs/wasm-parser" "^1.11.5" acorn "^8.7.1" acorn-import-assertions "^1.9.0" - browserslist "^4.14.5" + browserslist "^4.21.10" chrome-trace-event "^1.0.2" enhanced-resolve "^5.15.0" es-module-lexer "^1.2.1" @@ -13233,7 +13233,7 @@ webpack@^5.89.0: neo-async "^2.6.2" schema-utils "^3.2.0" tapable "^2.1.1" - terser-webpack-plugin "^5.3.7" + terser-webpack-plugin "^5.3.10" watchpack "^2.4.0" webpack-sources "^3.2.3" From 01d10a15c2b01986b9e1d16b431a4d600082b118 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BD=A0=E6=88=91=E7=9A=86=E5=87=A1=E4=BA=BA?= Date: Fri, 2 Feb 2024 16:12:43 +0000 Subject: [PATCH 50/55] Pontoon: Update Chinese (China) (zh-CN) localization of Firefox Profiler MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: 你我皆凡人 Co-authored-by: Olvcpr423 --- locales/zh-CN/app.ftl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/locales/zh-CN/app.ftl b/locales/zh-CN/app.ftl index d92f4e4cbd..f902a29640 100644 --- a/locales/zh-CN/app.ftl +++ b/locales/zh-CN/app.ftl @@ -25,7 +25,7 @@ AppHeader--app-header =
{ -profiler-brand-name }
{ -firefox-brand-name } 性能分析网页应用程序 AppHeader--github-icon = - .title = 前往我们的 Git 仓库(将打开新窗口) + .title = 前往我们的 Git 仓库(新建窗口打开) ## AppViewRouter ## This is used for displaying errors when loading the application. @@ -198,8 +198,8 @@ Home--documentation-button = 文档 Home--menu-button = 启用 { -profiler-brand-name } 菜单按钮 Home--menu-button-instructions = 启用分析器菜单按钮,即可在 { -firefox-brand-name } 中记录性能,然后进行剖析并分享至 profiler.firefox.com。 Home--profile-firefox-android-instructions = - 您还可以分析 { -firefox-android-brand-name }。 - 有关更多信息,请查阅此文档: + 您还可以分析 { -firefox-android-brand-name }, + 详见此文档: 直接在设备上分析 { -firefox-android-brand-name }。 # The word WebChannel should not be translated. # This message can be seen on https://main--perf-html.netlify.app/ in the tooltip From d17adb3b24782e97a46b0fa6b80b2dbff36c2ecc Mon Sep 17 00:00:00 2001 From: "depfu[bot]" <23717796+depfu[bot]@users.noreply.github.com> Date: Fri, 2 Feb 2024 22:55:28 +0000 Subject: [PATCH 51/55] =?UTF-8?q?=E2=AC=86=EF=B8=8F=20Update=20espree=20to?= =?UTF-8?q?=20version=2010.0.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 2 +- yarn.lock | 11 ++++++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index b3edd083d0..c051b7a8ee 100644 --- a/package.json +++ b/package.json @@ -132,7 +132,7 @@ "eslint-plugin-jest-formatting": "^3.1.0", "eslint-plugin-react": "^7.33.2", "eslint-plugin-testing-library": "^6.2.0", - "espree": "^9.6.1", + "espree": "^10.0.0", "fake-indexeddb": "^4.0.2", "fetch-mock-jest": "^1.5.1", "file-loader": "^6.2.0", diff --git a/yarn.lock b/yarn.lock index 2ab5b94a9b..475d4d3678 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2642,7 +2642,7 @@ acorn@^7.1.1: resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.0.tgz#e1ad486e6c54501634c6c397c5c121daa383607c" integrity sha512-+G7P8jJmCHr+S+cLfQxygbWhXy+8YTVGzAkpEbcLo2mLoL7tij/VG41QSHACSf5QgYRhMZYHuNc6drJaO0Da+w== -acorn@^8.0.0, acorn@^8.7.1, acorn@^8.8.2, acorn@^8.9.0: +acorn@^8.0.0, acorn@^8.11.3, acorn@^8.7.1, acorn@^8.8.2, acorn@^8.9.0: version "8.11.3" resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.11.3.tgz#71e0b14e13a4ec160724b38fb7b0f233b1b81d7a" integrity sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg== @@ -5173,6 +5173,15 @@ eslint@^8.56.0: strip-ansi "^6.0.1" text-table "^0.2.0" +espree@^10.0.0: + version "10.0.0" + resolved "https://registry.yarnpkg.com/espree/-/espree-10.0.0.tgz#ce1411bb31a514797dbd29ee360e4f3404643096" + integrity sha512-gdlKrfXQWv/3vubKqeQIiBUoWeknNQVEDpKD7OD3bC53g5EKISTuhcIoA1H1e+zqIuosdKrKuTDMmj8eFfhOnA== + dependencies: + acorn "^8.11.3" + acorn-jsx "^5.3.2" + eslint-visitor-keys "^3.4.1" + espree@^6.1.2: version "6.2.1" resolved "https://registry.yarnpkg.com/espree/-/espree-6.2.1.tgz#77fc72e1fd744a2052c20f38a5b575832e82734a" From f3dc7d66aee589c98bdbcc0689eff33b8077d7ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Naz=C4=B1m=20Can=20Alt=C4=B1nova?= Date: Wed, 31 Jan 2024 11:59:37 +0100 Subject: [PATCH 52/55] Return null instead of throwing an error if it fails to get the resource name in active tab view Previously if it was failing to find a resource name, it was throwing an error. This was happening because we are explicitly excluding the `about:blank` and `about:newtab` pages. They are excluded so we can find the correct page name. --- src/profile-logic/active-tab.js | 53 +++++++++++++++++++++++++-------- 1 file changed, 41 insertions(+), 12 deletions(-) diff --git a/src/profile-logic/active-tab.js b/src/profile-logic/active-tab.js index e4142acedb..f350437f8b 100644 --- a/src/profile-logic/active-tab.js +++ b/src/profile-logic/active-tab.js @@ -84,11 +84,17 @@ export function computeActiveTabTracks( mainTrackIndexes.push(threadIndex); } else { if (!isTabFilteredThreadEmpty(threadIndex, state)) { - resources.push({ - type: 'sub-frame', - threadIndex, - name: _getActiveTabResourceName(thread, innerWindowIDToPageMap), - }); + const resourceName = _getActiveTabResourceName( + thread, + innerWindowIDToPageMap + ); + if (resourceName !== null) { + resources.push({ + type: 'sub-frame', + threadIndex, + name: resourceName, + }); + } } } } else { @@ -96,11 +102,17 @@ export function computeActiveTabTracks( // track. Find out if that thread contains the active tab data, and add it // as a resource track if it does. if (!isTabFilteredThreadEmpty(threadIndex, state)) { - resources.push({ - type: 'thread', - threadIndex, - name: _getActiveTabResourceName(thread, innerWindowIDToPageMap), - }); + const resourceName = _getActiveTabResourceName( + thread, + innerWindowIDToPageMap + ); + if (resourceName !== null) { + resources.push({ + type: 'thread', + threadIndex, + name: resourceName, + }); + } } } @@ -189,11 +201,12 @@ function isTopmostThread( /** * Returns the name of active tab resource track. * It can be either a sub-frame or a regular thread. + * If it fails to find a name, returns null. */ function _getActiveTabResourceName( thread: Thread, innerWindowIDToPageMap: Map -): string { +): string | null { if (thread.isMainThread) { // This is a sub-frame. // Get the first innerWindowID inside the thread that's also present of innerWindowIDToPageMap. @@ -216,6 +229,16 @@ function _getActiveTabResourceName( // find them, so we can get the real resource name. const page = innerWindowIDToPageMap.get(data.innerWindowID); return ( + // During a page load, iframes usually start with an about:blank page + // and then they navigate to the url. While it's in about:blank, we + // might catch some markers. We would like to exclude them because + // `about:blank` name doesn't bring any value as a resource name and + // we would prefer to display the _real_ url instead which is + // captured after the about:blank. + // FIXME: I think we initially added `about:newtab` here to exclude + // the "new tab page -> website" navigation from showing the + // `about:newtab` all the time. But I don't think we have to worry + // about this for iframes. So maybe remove this? page && page.url !== 'about:blank' && page.url !== 'about:newtab' ); } @@ -225,9 +248,15 @@ function _getActiveTabResourceName( firstInnerWindowID = markerData.innerWindowID; } } + if (firstInnerWindowID === undefined || firstInnerWindowID === null) { - throw new Error('There must be an innerWindowID in the thread'); + // Since we excluded the about:blank and about:newtab pages, we might fail + // to find a valid innerWindowID. This might happen when an ad blocker + // blocks an iframe and therefore it fails to load. In that case, we + // should return null. + return null; } + const page = ensureExists(innerWindowIDToPageMap.get(firstInnerWindowID)); return page.url; } From cd5e6b7dc106bea8ebf78209c6636316e381efa1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Naz=C4=B1m=20Can=20Alt=C4=B1nova?= Date: Wed, 31 Jan 2024 12:00:58 +0100 Subject: [PATCH 53/55] Make getHumanReadableActiveTabTracks also print the resource tracks For some reason we weren't printing the resource tracks. This wasn't great because it's important to see all the resources there to be able to test the active tab correctly. --- src/test/fixtures/profiles/tracks.js | 17 +++++++++++++++++ src/test/store/active-tab.test.js | 1 + 2 files changed, 18 insertions(+) diff --git a/src/test/fixtures/profiles/tracks.js b/src/test/fixtures/profiles/tracks.js index 2829255f2e..c14ffee4ed 100644 --- a/src/test/fixtures/profiles/tracks.js +++ b/src/test/fixtures/profiles/tracks.js @@ -337,6 +337,7 @@ export function getStoreWithMemoryTrack(pid: Pid = '222') { */ export function getHumanReadableActiveTabTracks(state: State): string[] { const globalTracks = profileViewSelectors.getActiveTabGlobalTracks(state); + const resourceTracks = profileViewSelectors.getActiveTabResourceTracks(state); const selectedThreadIndexes = urlStateSelectors.getSelectedThreadIndexes(state); const text: string[] = []; @@ -369,6 +370,22 @@ export function getHumanReadableActiveTabTracks(state: State): string[] { } } + for (const resourceTrack of resourceTracks) { + switch (resourceTrack.type) { + case 'sub-frame': + text.push(` - iframe: ${resourceTrack.name}`); + break; + case 'thread': + text.push(` - ${resourceTrack.name}`); + break; + default: + throw assertExhaustiveCheck( + resourceTrack, + 'Unhandled ActiveTabResourceTrack.' + ); + } + } + return text; } diff --git a/src/test/store/active-tab.test.js b/src/test/store/active-tab.test.js index 8a5268f7b6..6739d69f7a 100644 --- a/src/test/store/active-tab.test.js +++ b/src/test/store/active-tab.test.js @@ -165,6 +165,7 @@ describe('ActiveTab', function () { const { getState } = setup(profile, false); expect(getHumanReadableActiveTabTracks(getState())).toEqual([ 'main track [tab] SELECTED', + ' - iframe: Page #2', ]); }); }); From 706ddfb673da3e22c202a98091d682c4feabf8b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Naz=C4=B1m=20Can=20Alt=C4=B1nova?= Date: Wed, 31 Jan 2024 12:01:23 +0100 Subject: [PATCH 54/55] Add a test for the new behavior to make sure that we are not crashing This test is crashing when you revert the first commit. I had to change the setup function to make sure that it doesn't overwrite the profile.pages array that we overwritten already with a custom page url. --- src/test/store/active-tab.test.js | 68 ++++++++++++++++++++++++++++++- 1 file changed, 66 insertions(+), 2 deletions(-) diff --git a/src/test/store/active-tab.test.js b/src/test/store/active-tab.test.js index 6739d69f7a..753956ca8f 100644 --- a/src/test/store/active-tab.test.js +++ b/src/test/store/active-tab.test.js @@ -26,12 +26,21 @@ import { getTimelineType, } from '../../selectors/url-state'; import { stateFromLocation } from '../../app-logic/url-handling'; +import { ensureExists } from '../../utils/flow'; import type { Profile } from 'firefox-profiler/types'; describe('ActiveTab', function () { - function setup(p = getProfileWithNiceTracks(), addInnerWindowID = true) { - const { profile, ...pageInfo } = addActiveTabInformationToProfile(p); + function setup( + p = getProfileWithNiceTracks(), + addInnerWindowID = true, + pageInfo = null + ) { + let profile = p; + if (!pageInfo) { + ({ profile, ...pageInfo } = addActiveTabInformationToProfile(profile)); + } + // Add the innerWindowIDs so we can compute the first thread as main track. if (addInnerWindowID) { profile.threads[0].frameTable.innerWindowID[0] = @@ -168,6 +177,61 @@ describe('ActiveTab', function () { ' - iframe: Page #2', ]); }); + + it('do not show the thread when it fails to find the iframe resource name', function () { + const { + profile: p, + funcNamesDictPerThread: [firstThread], + } = getProfileFromTextSamples( + // First thread is the main thread of the first tab (which is the + // active tab) + `A`, + // The second thread is an iframe of the first thread. + `B` + ); + + p.threads[0].name = 'GeckoMain'; + p.threads[0].isMainThread = true; + p.threads[1].name = 'GeckoMain'; + p.threads[1].isMainThread = true; + + const { profile, ...pageInfo } = addActiveTabInformationToProfile(p); + addInnerWindowIdToStacks( + profile.threads[0], + /* listOfOperations */ + [ + // first tab is the active tab. + { + innerWindowID: pageInfo.firstTabInnerWindowIDs[0], + callNodes: [[firstThread.A]], + }, + ] + ); + addMarkersToThreadWithCorrespondingSamples(profile.threads[1], [ + // All about:blank or about:newtab markers are ignored during the + // track name computation because they don't provide the correct innerWindowID. + // This thread SHOULD NOT be shown in the tracks. + [ + 'This marker will be filtered', + 1, + 2, + { + type: 'tracing', + category: 'Navigation', + innerWindowID: pageInfo.iframeInnerWindowIDsWithChild, + }, + ], + ]); + + // Lastly, we need to put the iframe innerWindowID url to about:blank to test this case. + ensureExists(profile.pages)[1].url = 'about:blank'; + + const { getState } = setup(profile, false, pageInfo); + + expect(getHumanReadableActiveTabTracks(getState())).toEqual([ + 'main track [tab] SELECTED', + ]); + }); }); }); From ec8662e47a9c204c79c36236496cfbe0929ab5d9 Mon Sep 17 00:00:00 2001 From: Julien Wajsberg Date: Tue, 6 Feb 2024 17:11:03 +0100 Subject: [PATCH 55/55] Update node to v18.19 --- .circleci/config.yml | 2 +- .nvmrc | 2 +- appveyor.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index dea93e7e5a..1498c35e50 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -3,7 +3,7 @@ version: 2.1 executors: node: docker: - - image: cimg/node:18.16 + - image: cimg/node:18.19 base: docker: - image: cimg/base:stable diff --git a/.nvmrc b/.nvmrc index b492b08635..df5f0bcd6d 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -18.16 +18.19 diff --git a/appveyor.yml b/appveyor.yml index 8bad0f0d07..4377874a1d 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,6 +1,6 @@ clone_depth: 5 environment: - nodejs_version: '18.16' + nodejs_version: '18.19' platform: x64 # flow needs 64b platforms branches: