Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add a command to download the Hermes Sampling Profiler to a local machine #1246

Merged
merged 68 commits into from
Sep 4, 2020
Merged
Show file tree
Hide file tree
Changes from 63 commits
Commits
Show all changes
68 commits
Select commit Hold shift + click to select a range
1167db0
added adb commands
jessieAnhNguyen Jul 7, 2020
2047721
feat: implemented download hermes profile command
jessieAnhNguyen Jul 21, 2020
4af2cbe
deleted unused adbkit packages
jessieAnhNguyen Jul 22, 2020
98e1ecf
changed files excluded node modules
jessieAnhNguyen Jul 22, 2020
24a248b
handled edge case when there's no file
jessieAnhNguyen Jul 22, 2020
b720baf
deleted unnecessary changes
jessieAnhNguyen Jul 22, 2020
4c0fb45
deleted unnecessary changes 2
jessieAnhNguyen Jul 22, 2020
defc7b7
Merge pull request #2 from MLH-Fellowship/jessie
jessieAnhNguyen Jul 27, 2020
b5d1d3c
added transformer files
jessieAnhNguyen Jul 28, 2020
89837ae
implemented --verbose flag
jessieAnhNguyen Jul 28, 2020
23cd80c
Merge branch 'jessie-cli2' into test/transformer
jessieAnhNguyen Jul 28, 2020
3e7c6f4
integrated transformer tool with CLI
jessieAnhNguyen Jul 28, 2020
cbc0c28
implemented --raw flag
jessieAnhNguyen Jul 28, 2020
28a55d4
deleted changelog comments and fixed small changes
jessieAnhNguyen Jul 30, 2020
680eccb
changed wording
jessieAnhNguyen Jul 30, 2020
ef6bb10
deleted unused event types and remove unused dev dependencies
jessieAnhNguyen Jul 30, 2020
2285b38
generated source map through
jessieAnhNguyen Aug 6, 2020
86c8110
Merge pull request #4 from MLH-Fellowship/test/transformer
jessieAnhNguyen Aug 10, 2020
652290d
fixed SHA-1 error
jessieAnhNguyen Aug 10, 2020
deffdd2
Merge branch 'master' of https://github.com/MLH-Fellowship/cli into f…
jessieAnhNguyen Aug 10, 2020
f560481
implemented the source map steps
jessieAnhNguyen Aug 10, 2020
e3fe062
added transformer package and profile-hermes.md
jessieAnhNguyen Aug 11, 2020
1cff3c8
Merge pull request #6 from MLH-Fellowship/feat/generate-source-map
jevakallio Aug 11, 2020
ca07916
Rename command to profile-hermes
jessieAnhNguyen Aug 11, 2020
867b6b2
Improve command line output examples
jessieAnhNguyen Aug 11, 2020
7ee8fc6
Extract source map functionality to its own file
jessieAnhNguyen Aug 11, 2020
4e0dd95
Readability improvements (@todo: fix @TODO comments)
jessieAnhNguyen Aug 11, 2020
c422253
upgrade hermes-profile-transformer package
jessieAnhNguyen Aug 11, 2020
3d9d04e
implemented @TODO: remove axios and use node-fetch
jessieAnhNguyen Aug 13, 2020
29e55ca
deleted small unintended changes and removed axios
jessieAnhNguyen Aug 13, 2020
1078bdb
removed axios package and deleted unnecessary changes
jessieAnhNguyen Aug 13, 2020
e050ab2
removed source-map related packages
jessieAnhNguyen Aug 13, 2020
b955db9
added new line after each logger
jessieAnhNguyen Aug 13, 2020
e086ec9
Merge pull request #7 from MLH-Fellowship/chore/pre-publish
jessieAnhNguyen Aug 13, 2020
423424f
Merge branch 'master' of https://github.com/MLH-Fellowship/cli
jessieAnhNguyen Aug 13, 2020
b37a804
upgraded hermes-profile-transformer package to latest
jessieAnhNguyen Aug 14, 2020
2f93778
moved profile-hermes folder out of cli to a separate package cli-hermes
jessieAnhNguyen Aug 18, 2020
ed90c7e
deleted the command from projectCommands in cli
jessieAnhNguyen Aug 18, 2020
aacd874
re-added the command in the projectCommands
saphal1998 Aug 18, 2020
86cba71
removed unused dependencies from cli
saphal1998 Aug 18, 2020
48b1b0f
added cli-hermes to the list of yarn link
jessieAnhNguyen Aug 18, 2020
e53ee19
Merge pull request #9 from MLH-Fellowship/movePackage
jessieAnhNguyen Aug 18, 2020
e3ff688
removed unused @types/ip dependency
jessieAnhNguyen Aug 18, 2020
f5e51e1
Merge remote-tracking branch 'upstream/master'
jessieAnhNguyen Aug 18, 2020
a0ad665
added @types/ip dependency in cli-hermes
jessieAnhNguyen Aug 18, 2020
1735396
moved @types/ip to dev dependency
jessieAnhNguyen Aug 18, 2020
1b0438b
added type definition for cli-hermes
saphal1998 Aug 18, 2020
5ebb4ba
fixed type definition
jessieAnhNguyen Aug 18, 2020
236b038
Added cli-hermes ref to tsconfig in packages/cli
saphal1998 Aug 18, 2020
8da90e5
Merge branch 'master' of github.com:MLH-Fellowship/cli
saphal1998 Aug 18, 2020
f7cb658
Removed unnecessary code comment
jessieAnhNguyen Aug 19, 2020
c85506e
made changes based on PR review
jessieAnhNguyen Aug 19, 2020
125836c
Merge branch 'master' of https://github.com/MLH-Fellowship/cli
jessieAnhNguyen Aug 19, 2020
b7dbb5e
added a verbose log
jessieAnhNguyen Aug 19, 2020
27f14d9
added cli-platform-android as a dependency
jessieAnhNguyen Aug 19, 2020
02263ae
added cli-platform-android as a dependency in cli
jessieAnhNguyen Aug 19, 2020
8fc1b01
Fixing path in tsconfig
saphal1998 Aug 19, 2020
bfc08b2
Merge conflict
saphal1998 Aug 19, 2020
4478249
Added type defs of platform-android to cli-hermes, path fixed
saphal1998 Aug 19, 2020
93acfcd
Fixed chalk version on cli-hermes
saphal1998 Aug 19, 2020
9607160
removed unnecessary comment
jessieAnhNguyen Aug 21, 2020
4e50329
Merge remote-tracking branch 'upstream/master'
jessieAnhNguyen Aug 21, 2020
dcabe01
Merge branch 'master' of https://github.com/MLH-Fellowship/cli
jessieAnhNguyen Aug 21, 2020
3e97a31
changed --filename flag and throw real error
jessieAnhNguyen Sep 1, 2020
f4b4041
Merge pull request #10 from MLH-Fellowship/review/edit
jessieAnhNguyen Sep 1, 2020
75095c5
Fix catch block in profile-hermes/index to match the error thrown
saphal1998 Sep 3, 2020
32a917e
Typecast error as CLIError
saphal1998 Sep 3, 2020
8d6fb8f
Apply suggestions from code review
saphal1998 Sep 4, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ This repository is split into two packages:
_Note: you must use the `--watchFolders` flag with the `start` command when testing the CLI with `yarn link` like this. Otherwise Metro can't find the symlinked folder and this may result in errors such as `ReferenceError: SHA-1 for file ... is not computed`._

### Setup

Because of a modular design of the CLI, we recommend developing using symbolic links to its packages. This way you can use it seamlessly in the tested project, as you'd use the locally installed CLI. Here's what you need to run in the terminal:

#### yarn v1
Expand All @@ -33,10 +34,12 @@ Because of a modular design of the CLI, we recommend developing using symbolic l
cd /path/to/cloned/cli/
yarn link-packages
```

And then:

```sh
cd /my/new/react-native/project/
yarn link "@react-native-community/cli-platform-ios" "@react-native-community/cli-platform-android" "@react-native-community/cli" "@react-native-community/cli-server-api" "@react-native-community/cli-types" "@react-native-community/cli-tools" "@react-native-community/cli-debugger-ui"
yarn link "@react-native-community/cli-platform-ios" "@react-native-community/cli-platform-android" "@react-native-community/cli" "@react-native-community/cli-server-api" "@react-native-community/cli-types" "@react-native-community/cli-tools" "@react-native-community/cli-debugger-ui" "@react-native-community/cli-hermes"
```

Once you're done with testing and you'd like to get back to regular setup, run `yarn unlink` instead of `yarn link` from above command. Then `yarn install --force`.
Expand All @@ -51,6 +54,7 @@ yarn link /path/to/cloned/cli/ --all
When you'd like to revert to a regular setup, you will need to revert the changes made to the `resolutions` field of `package.json`.

### Running

```sh
yarn react-native start --watchFolders /path/to/cloned/cli/
yarn react-native run-android
Expand Down
46 changes: 46 additions & 0 deletions docs/commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ React Native CLI comes with following commands:
- [`uninstall`](#uninstall)
- [`unlink`](#unlink)
- [`upgrade`](#upgrade)
- [`profile-hermes`](#profile-hermes)

### `bundle`

Expand Down Expand Up @@ -546,3 +547,48 @@ Upgrade your app's template files to the specified or latest npm version using [
Using this command is a recommended way of upgrading relatively simple React Native apps with not too many native libraries linked. The more iOS and Android build files are modified, the higher chance for a conflicts. The command will guide you on how to continue upgrade process manually in case of failure.

_Note: If you'd like to upgrade using this method from React Native version lower than 0.59.0, you may use a standalone version of this CLI: `npx @react-native-community/cli upgrade`._

### `profile-hermes`

Usage:

```sh
react-native profile-hermes [destinationDir] <flag>
```

Pull and convert a Hermes tracing profile to Chrome tracing profile, then store it in the directory <destinationDir> of the local machine.

- `destinationDir` is optional, if provided, pull the file to that directory
> default: pull to the current React Native app root directory

#### Options

#### `--fileName [string]`
thymikee marked this conversation as resolved.
Show resolved Hide resolved
saphal1998 marked this conversation as resolved.
Show resolved Hide resolved

File name of the profile to be downloaded, eg. sampling-profiler-trace8593107139682635366.cpuprofile.

> default: pull the latest file

#### `--raw`

Pulls the original Hermes tracing profile without any transformation

#### `--sourcemap-path [string]`

The local path to your source map file if you generated it manually, ex. `/tmp/sourcemap.json`

#### `--generate-sourcemap`

Generate the JS bundle and source map in `os.tmpdir()`

#### '--port [number]',

The running metro server port number

> default: 8081

### Notes on source map

This step is recommended in order for the source map to be generated:

If you are planning on building a debug APK, that will run without the packager, by invoking `./gradlew assembleDebug` you can simply set `bundleInDebug: true` in your app/build.gradle file, inside the `project.ext.react` map.
26 changes: 26 additions & 0 deletions packages/cli-hermes/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"name": "@react-native-community/cli-hermes",
"version": "4.11.0",
"license": "MIT",
"main": "build/index.js",
"publishConfig": {
"access": "public"
},
"types": "build/index.d.ts",
"dependencies": {
"@react-native-community/cli-tools": "^4.11.0",
"@react-native-community/cli-platform-android": "^4.11.0",
"chalk": "^3.0.0",
"hermes-profile-transformer": "^0.0.6",
jessieAnhNguyen marked this conversation as resolved.
Show resolved Hide resolved
"ip": "^1.1.5"
},
"files": [
"build",
"!*.d.ts",
"!*.map"
],
"devDependencies": {
"@react-native-community/cli-types": "^4.10.1",
"@types/ip": "^1.1.0"
}
}
2 changes: 2 additions & 0 deletions packages/cli-hermes/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
import profileHermes from './profileHermes/index';
export default profileHermes;
128 changes: 128 additions & 0 deletions packages/cli-hermes/src/profileHermes/downloadProfile.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
import {Config} from '@react-native-community/cli-types';
import {execSync} from 'child_process';
import {logger, CLIError} from '@react-native-community/cli-tools';
import fs from 'fs';
import path from 'path';
import os from 'os';
import transformer from 'hermes-profile-transformer';
import {findSourcemap, generateSourcemap} from './sourcemapUtils';
import {
getAndroidProject,
getPackageName,
} from '@react-native-community/cli-platform-android';
/**
* Get the last modified hermes profile
* @param packageName
*/
function getLatestFile(packageName: string): string {
try {
const file = execSync(`adb shell run-as ${packageName} ls cache/ -tp | grep -v /$ | egrep '\.cpuprofile' | head -1
`);
return file.toString().trim();
} catch (e) {
throw new Error(e);
}
}

function execSyncWithLog(command: string) {
logger.debug(`${command}`);
return execSync(command);
}

/**
* Pull and convert a Hermes tracing profile to Chrome tracing profile
* @param ctx
* @param dstPath
* @param fileName
* @param sourceMapPath
* @param raw
* @param generateSourceMap
*/
export async function downloadProfile(
ctx: Config,
dstPath: string,
fileName?: string,
sourcemapPath?: string,
raw?: boolean,
shouldGenerateSourcemap?: boolean,
port?: string,
) {
try {
const androidProject = getAndroidProject(ctx);
thymikee marked this conversation as resolved.
Show resolved Hide resolved
const packageName = getPackageName(androidProject);

// If file name is not specified, pull the latest file from device
const file = fileName || (await getLatestFile(packageName));
if (!file) {
throw new CLIError(
'There is no file in the cache/ directory. Did you record a profile from the developer menu?',
);
}

logger.info(`File to be pulled: ${file}`);

// If destination path is not specified, pull to the current directory
dstPath = dstPath || ctx.root;

logger.debug('Internal commands run to pull the file:');

// Copy the file from device's data to sdcard, then pull the file to a temp directory
execSyncWithLog(`adb shell run-as ${packageName} cp cache/${file} /sdcard`);

// If --raw, pull the hermes profile to dstPath
if (raw) {
execSyncWithLog(`adb pull /sdcard/${file} ${dstPath}`);
logger.success(`Successfully pulled the file to ${dstPath}/${file}`);
}

// Else: transform the profile to Chrome format and pull it to dstPath
else {
const osTmpDir = os.tmpdir();
const tempFilePath = path.join(osTmpDir, file);

execSyncWithLog(`adb pull /sdcard/${file} ${tempFilePath}`);

// If path to source map is not given
if (!sourcemapPath) {
// Get or generate the source map
if (shouldGenerateSourcemap) {
sourcemapPath = await generateSourcemap(port);
} else {
sourcemapPath = await findSourcemap(ctx, port);
}

// Run without source map
if (!sourcemapPath) {
logger.warn(
'Cannot find source maps, running the transformer without it',
);
logger.info(
'Instructions on how to get source maps: set `bundleInDebug: true` in your app/build.gradle file, inside the `project.ext.react` map.',
);
}
}

// Run transformer tool to convert from Hermes to Chrome format
const events = await transformer(
tempFilePath,
sourcemapPath,
'index.bundle',
);

const transformedFilePath = `${dstPath}/${path.basename(
file,
'.cpuprofile',
)}-converted.json`;
fs.writeFileSync(
transformedFilePath,
JSON.stringify(events, undefined, 4),
'utf-8',
);
logger.success(
`Successfully converted to Chrome tracing format and pulled the file to ${transformedFilePath}`,
);
}
} catch (e) {
throw new CLIError('Failed to download the sampling profiler');
}
}
77 changes: 77 additions & 0 deletions packages/cli-hermes/src/profileHermes/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import {logger, CLIError} from '@react-native-community/cli-tools';
import {Config} from '@react-native-community/cli-types';
import {downloadProfile} from './downloadProfile';

type Options = {
fileName?: string;
raw?: boolean;
sourcemapPath?: string;
generateSourcemap?: boolean;
port?: string;
};

async function profileHermes(
[dstPath]: Array<string>,
ctx: Config,
options: Options,
) {
try {
logger.info(
'Downloading a Hermes Sampling Profiler from your Android device...',
);
if (!options.fileName) {
logger.info('No filename is provided, pulling latest file');
}
await downloadProfile(
ctx,
dstPath,
options.fileName,
options.sourcemapPath,
options.raw,
options.generateSourcemap,
options.port,
);
} catch (err) {
throw new CLIError('Failed to download the Hermes Sampling Profile.', err);
}
}

export default {
name: 'profile-hermes [destinationDir]',
description:
'Pull and convert a Hermes tracing profile to Chrome tracing profile, then store it in the directory <destinationDir> of the local machine',
func: profileHermes,
options: [
{
name: '--fileName [string]',
description:
'File name of the profile to be downloaded, eg. sampling-profiler-trace8593107139682635366.cpuprofile',
},
{
name: '--raw',
description:
'Pulls the original Hermes tracing profile without any transformation',
},
{
name: '--sourcemap-path [string]',
description:
'The local path to your source map file, eg. /tmp/sourcemap.json',
},
{
name: '--generate-sourcemap',
description: 'Generates the JS bundle and source map',
},
{
name: '--port [number]',
default: process.env.RCT_METRO_PORT || 8081,
parse: (val: number) => String(val),
thymikee marked this conversation as resolved.
Show resolved Hide resolved
},
],
examples: [
{
desc:
'Download the Hermes Sampling Profiler to the directory <destinationDir> on the local machine',
cmd: 'profile-hermes /tmp',
},
],
};
Loading