From a1cc7986a711e8b8ea1da07ddfec476c2c97c58f Mon Sep 17 00:00:00 2001 From: Lenz Weber-Tronic Date: Tue, 20 Jun 2023 13:39:32 +0200 Subject: [PATCH] download correct sourceMap for dev/prod profiles (#1833) add warning for Expo Dev menu profiles Co-authored-by: Lenz Weber-Tronic --- .../src/profileHermes/downloadProfile.ts | 10 ++- .../src/profileHermes/metroBundleOptions.ts | 63 +++++++++++++++++++ .../src/profileHermes/sourcemapUtils.ts | 19 +++--- 3 files changed, 81 insertions(+), 11 deletions(-) create mode 100644 packages/cli-hermes/src/profileHermes/metroBundleOptions.ts diff --git a/packages/cli-hermes/src/profileHermes/downloadProfile.ts b/packages/cli-hermes/src/profileHermes/downloadProfile.ts index 7dbd0fdfb..8b843dfd7 100644 --- a/packages/cli-hermes/src/profileHermes/downloadProfile.ts +++ b/packages/cli-hermes/src/profileHermes/downloadProfile.ts @@ -7,6 +7,7 @@ import os from 'os'; import transformer from 'hermes-profile-transformer'; import {findSourcemap, generateSourcemap} from './sourcemapUtils'; import {getAndroidProject} from '@react-native-community/cli-platform-android'; +import {getMetroBundleOptions} from './metroBundleOptions'; /** * Get the last modified hermes profile * @param packageNameWithSuffix @@ -44,7 +45,7 @@ export async function downloadProfile( sourcemapPath?: string, raw?: boolean, shouldGenerateSourcemap?: boolean, - port?: string, + port: string = '8081', appId?: string, appIdSuffix?: string, ) { @@ -88,13 +89,16 @@ export async function downloadProfile( execSyncWithLog( `adb shell run-as ${packageNameWithSuffix} cat cache/${file} > ${tempFilePath}`, ); + + const bundleOptions = getMetroBundleOptions(tempFilePath); + // If path to source map is not given if (!sourcemapPath) { // Get or generate the source map if (shouldGenerateSourcemap) { - sourcemapPath = await generateSourcemap(port); + sourcemapPath = await generateSourcemap(port, bundleOptions); } else { - sourcemapPath = await findSourcemap(ctx, port); + sourcemapPath = await findSourcemap(ctx, port, bundleOptions); } // Run without source map diff --git a/packages/cli-hermes/src/profileHermes/metroBundleOptions.ts b/packages/cli-hermes/src/profileHermes/metroBundleOptions.ts new file mode 100644 index 000000000..74c77c1ba --- /dev/null +++ b/packages/cli-hermes/src/profileHermes/metroBundleOptions.ts @@ -0,0 +1,63 @@ +import {logger} from '@react-native-community/cli-tools'; +import fs from 'fs'; +import type {HermesCPUProfile} from 'hermes-profile-transformer/dist/types/HermesProfile'; + +export interface MetroBundleOptions { + platform: string; + dev: boolean; + minify: boolean; +} + +export function getMetroBundleOptions( + downloadedProfileFilePath: string, +): MetroBundleOptions { + let options: MetroBundleOptions = { + platform: 'android', + dev: true, + minify: false, + }; + + try { + const contents: HermesCPUProfile = JSON.parse( + fs.readFileSync(downloadedProfileFilePath, { + encoding: 'utf8', + }), + ); + const matchBundleUrl = /^.*\((.*index\.bundle.*)\)/; + let containsExpoDevMenu = false; + let hadMatch = false; + for (const frame of Object.values(contents.stackFrames)) { + if (frame.name.includes('EXDevMenuApp')) { + containsExpoDevMenu = true; + } + const match = matchBundleUrl.exec(frame.name); + if (match) { + const parsed = new URL(match[1]); + const platform = parsed.searchParams.get('platform'), + dev = parsed.searchParams.get('dev'), + minify = parsed.searchParams.get('minify'); + if (platform) { + options.platform = platform; + } + if (dev) { + options.dev = dev === 'true'; + } + if (minify) { + options.minify = minify === 'true'; + } + + hadMatch = true; + break; + } + } + if (containsExpoDevMenu && !hadMatch) { + logger.warn(`Found references to the Expo Dev Menu in your profiling sample. +You might have accidentally recorded the Expo Dev Menu instead of your own application. +To work around this, please reload your app twice before starting a profiler recording.`); + } + } catch (e) { + throw e; + } + + return options; +} diff --git a/packages/cli-hermes/src/profileHermes/sourcemapUtils.ts b/packages/cli-hermes/src/profileHermes/sourcemapUtils.ts index dedec71b6..4a8c6107b 100644 --- a/packages/cli-hermes/src/profileHermes/sourcemapUtils.ts +++ b/packages/cli-hermes/src/profileHermes/sourcemapUtils.ts @@ -5,6 +5,7 @@ import path from 'path'; import os from 'os'; import {SourceMap} from 'hermes-profile-transformer'; import ip from 'ip'; +import {MetroBundleOptions} from './metroBundleOptions'; function getTempFilePath(filename: string) { return path.join(os.tmpdir(), filename); @@ -29,14 +30,14 @@ function writeJsonSync(targetPath: string, data: any) { } async function getSourcemapFromServer( - port?: string, + port: string, + {platform, dev, minify}: MetroBundleOptions, ): Promise { logger.debug('Getting source maps from Metro packager server'); - const DEBUG_SERVER_PORT = port || '8081'; const IP_ADDRESS = ip.address(); - const PLATFORM = 'android'; - const requestURL = `http://${IP_ADDRESS}:${DEBUG_SERVER_PORT}/index.map?platform=${PLATFORM}&dev=true`; + const requestURL = `http://${IP_ADDRESS}:${port}/index.map?platform=${platform}&dev=${dev}&minify=${minify}`; + logger.debug(`Downloading from ${requestURL}`); try { const {data} = await fetch(requestURL); return data as SourceMap; @@ -50,11 +51,12 @@ async function getSourcemapFromServer( * Generate a sourcemap by fetching it from a running metro server */ export async function generateSourcemap( - port?: string, + port: string, + bundleOptions: MetroBundleOptions, ): Promise { // Fetch the source map to a temp directory const sourceMapPath = getTempFilePath('index.map'); - const sourceMapResult = await getSourcemapFromServer(port); + const sourceMapResult = await getSourcemapFromServer(port, bundleOptions); if (sourceMapResult) { logger.debug('Using source maps from Metro packager server'); @@ -75,7 +77,8 @@ export async function generateSourcemap( */ export async function findSourcemap( ctx: Config, - port?: string, + port: string, + bundleOptions: MetroBundleOptions, ): Promise { const intermediateBuildPath = path.join( ctx.root, @@ -108,6 +111,6 @@ export async function findSourcemap( logger.debug(`Getting the source map from ${intermediateBuildPath}`); return intermediateBuildPath; } else { - return generateSourcemap(port); + return generateSourcemap(port, bundleOptions); } }