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

Add documentation for Hermes sampling profiler #2159

Merged
merged 21 commits into from
Sep 24, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
73fdb73
added the profile-hermes.md file
jessieAnhNguyen Aug 18, 2020
1e7aaaf
Added information about hermes-profile-transformer
saphal1998 Aug 19, 2020
6d895bc
Added hermes profile transformer section - with assets
saphal1998 Aug 19, 2020
802b3fb
final fixes
jessieAnhNguyen Aug 20, 2020
c1ca7d9
Merge pull request #42 from MLH-Fellowship/doc/profile-hermes
jessieAnhNguyen Aug 20, 2020
a0068db
deleted comments
jessieAnhNguyen Aug 20, 2020
42edbf5
changed code block style
jessieAnhNguyen Aug 20, 2020
5657396
changed code block style of build.gradle
jessieAnhNguyen Aug 20, 2020
60c38f0
Fix lines, removed commas near markdown headings
saphal1998 Aug 20, 2020
ae27c83
Merge branch 'master' into bug/lines-fix
saphal1998 Aug 20, 2020
3ce4e6c
Merge pull request #44 from MLH-Fellowship/bug/lines-fix
saphal1998 Aug 20, 2020
64d3011
Tweaking hermes profiling guide
rachelnabors Aug 20, 2020
8660ff0
Merge pull request #45 from rachelnabors/patch-2
jessieAnhNguyen Aug 21, 2020
0d1c9eb
edited wording
jessieAnhNguyen Aug 21, 2020
42036ea
Merge pull request #46 from MLH-Fellowship/doc/profile-hermes
jessieAnhNguyen Aug 21, 2020
99da4ec
Merge remote-tracking branch 'upstream/master'
jessieAnhNguyen Aug 21, 2020
a615f35
merged upstream/master and moved pictures to website/static/docs/assets
jessieAnhNguyen Aug 21, 2020
982450c
More tweaks to verbiage
rachelnabors Sep 2, 2020
1ea9981
Merge pull request #49 from rachelnabors/patch-3
saphal1998 Sep 11, 2020
896a4ea
removed open developer menu picture
jessieAnhNguyen Sep 15, 2020
c1c0fc2
Merge pull request #50 from MLH-Fellowship/jessie/edit
jessieAnhNguyen Sep 15, 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
155 changes: 155 additions & 0 deletions docs/profile-hermes.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
---
id: profile-hermes
title: Profiling with Hermes
---

You can visualize JavaScript's performance in a React Native app using [Hermes](https://github.com/facebook/hermes). Hermes is a small and lightweight JavaScript engine optimized for running React Native on Android (you can [read more about using it with React Native here](hermes). Hermes helps improve app performance and also exposes ways to analyze the performance of the JavaScript that it runs.

In this section, you will learn how to profile your React Native app running on Hermes and how to visualize the profile using [the Performance tab on Chrome DevTools](https://developers.google.com/web/tools/chrome-devtools/evaluate-performance/reference)

> Be sure to [enable hermes in your app](hermes) before you get started!

Follow the instructions below to get started profiling:

1. [Record a Hermes sampling profile](profile-hermes.md#record-a-hermes-sampling-profile)
2. [Execute command from CLI](profile-hermes.md#execute-command-from-cli)
3. [Open the downloaded profile on Chrome DevTools](profile-hermes.md#open-the-downloaded-profile-on-chrome-devtools)

## Record a Hermes sampling profile

To record a sampling profiler from the Developer Menu:

1. Navigate to your running Metro server terminal.
2. Press `d` to open the **Developer Menu.**
3. Select **Enable Sampling Profiler.**
4. Execute your JavaScript by in your app (press buttons, etc.)
5. Open the **Developer Menu** by pressing `d` again.
6. Select **Disable Sampling Profiler** to stop recording and save the sampling profiler.

A toast will show the location where the sampling profiler has been saved, usually in `/data/user/0/com.appName/cache/*.cpuprofile`

<img src="/docs/assets/HermesProfileSaved.png" height=465 width=250 alt="Toast Notification of Profile saving">

## Execute command from CLI

You can use the [React Native CLI](https://github.com/react-native-community/cli) to convert the Hermes tracing profile to Chrome tracing profile, and then pull it to your local machine using:

```sh
npx react-native profile-hermes [destinationDir]
```

### Enabling source map

A source map is used to enhance the profile and associate trace events with the application code. You can automatically generate a source map when converting the Hermes tracing profile to a Chrome tracing profile by enabling `bundleInDebug` if the app is running in development mode. This allows React Native to build the bundle during its running process. Here's how:

1. In your app's `android/app/build.gradle` file, add:

```java
project.ext.react = [
bundleInDebug: true,
]
```

> Be sure to clean the build whenever you make any changes to `build.gradle`

2. Clean the build by running:

```sh
cd android && ./gradlew clean
```

3. Run your app:

```sh
npx react-native run-android
```

### Common errors

#### `adb: no devices/emulators found` or `adb: device offline`

- **Why this happens** The CLI cannot access the device or emulator (through adb) you are using to run the app.
- **How to fix** Make sure your Android device/emulator is connected and running. The command only works when it can access adb.

#### `There is no file in the cache/ directory`

- **Why this happens** The CLI cannot find any **.cpuprofile** file in your app's **cache/** directory. You might have forgotten to record a profile from the device.
- **How to fix** Follow the [instructions](profile-hermes.md#record-a-hermes-sampling-profile) to enable/disable profiler from device.

#### `Error: your_profile_name.cpuprofile is an empty file`

- **Why this happens** The profile is empty, it might be because Hermes is not running correctly.
- **How to fix** Make sure your app is running on the latest version of Hermes.

## Open the downloaded profile in Chrome DevTools

To open the profile in Chrome DevTools:

1. Open Chrome DevTools.
2. Select the **Performance** tab.
3. Right click and choose **Load profile...**

<img src="/docs/assets/openChromeProfile.png" alt="Loading a performance profile on Chrome DevTools">

## How does the Hermes Profile Transformer work?

The Hermes Sample Profile is of the `JSON object format`, while the format that Google's DevTools supports is `JSON Array Format`. (More information about the formats can be found on the [Trace Event Format Document](https://docs.google.com/document/d/1CvAClvFfyA5R-PhYUmn5OOQtYMH4h6I0nSsKchNAySU/preview))

```ts
export interface HermesCPUProfile {
traceEvents: SharedEventProperties[];
samples: HermesSample[];
stackFrames: { [key in string]: HermesStackFrame };
}
```

The Hermes profile has most of its information encoded into the `samples` and `stackFrames` properties. Each sample is a snapshot of the function call stack at that particular timestamp as each sample has a `sf` property which corresponds to a function call.

```ts
export interface HermesSample {
cpu: string;
name: string;
ts: string;
pid: number;
tid: string;
weight: string;
/**
* Will refer to an element in the stackFrames object of the Hermes Profile
*/
sf: number;
stackFrameData?: HermesStackFrame;
}
```

The information about a function call can be found in `stackFrames` which contains key-object pairs, where the key is the `sf` number and the corresponding object gives us all the relevant information about the function including the `sf` number of its parent function. This parent-child relationship can be traced upwards to find the information of all the functions running at a particular timestamp.

```ts
export interface HermesStackFrame {
line: string;
column: string;
funcLine: string;
funcColumn: string;
name: string;
category: string;
/**
* A parent function may or may not exist
*/
parent?: number;
}
```

At this point, you should define a few more terms, namely:

1. Nodes: The objects corresponding to `sf` numbers in `stackFrames`
2. Active Nodes: The nodes which are currently running at a particular timestamp. A node is classified as running if its `sf` number is in the function call stack. This call stack can be obtained from the `sf` number of the sample and tracing upwards till parent `sf`s are available

The `samples` and the `stackFrames` in tandem can then be used to generate all the start and end events at the corresponding timestamps, wherein:

1. Start Nodes/Events: Nodes absent in the previous sample's function call stack but present in the current sample's.
2. End Nodes/Events: Nodes present in the previous sample's function call stack but absent in the current sample's.

<img src="/docs/assets/CallStackDemo.jpg" height=800 width=600 alt="CallStack Terms Explained">

You can now construct a `flamechart` of function calls as you have all the function information including its start and end timestamps.

The `hermes-profile-transformer` can convert any profile generated using Hermes into a format that can be directly displayed in Chrome DevTools. More information about this can be found on [ `@react-native-community/hermes-profile-transformer` ](https://github.com/react-native-community/hermes-profile-transformer)
6 changes: 6 additions & 0 deletions website/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,9 @@
"pressevent": {
"title": "PressEvent Object Type"
},
"profile-hermes": {
"title": "Profiling with Hermes"
},
"profiling": {
"title": "Profiling"
},
Expand All @@ -284,6 +287,9 @@
"props": {
"title": "Props"
},
"publishing-to-app-store": {
"title": "Publishing to Apple App Store"
},
"pushnotificationios": {
"title": "🚧 PushNotificationIOS"
},
Expand Down
3 changes: 2 additions & 1 deletion website/sidebars.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@
"performance",
"optimizing-flatlist-configuration",
"ram-bundles-inline-requires",
"profiling"
"profiling",
"profile-hermes"
],
"JavaScript Runtime": ["javascript-environment", "timers", "hermes"],
"Connectivity": ["network", "security"],
Expand Down
Binary file added website/static/docs/assets/CallStackDemo.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added website/static/docs/assets/openChromeProfile.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
28 changes: 14 additions & 14 deletions website/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
dependencies:
"@babel/highlight" "^7.0.0"

"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4", "@babel/code-frame@^7.8.3":
"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4":
version "7.10.4"
resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.10.4.tgz#168da1a36e90da68ae8d49c0f1b48c7c6249213a"
integrity sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==
Expand Down Expand Up @@ -47,7 +47,7 @@
semver "^5.4.1"
source-map "^0.5.0"

"@babel/generator@^7.11.0", "@babel/generator@^7.8.4":
"@babel/generator@^7.11.0":
version "7.11.0"
resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.11.0.tgz#4b90c78d8c12825024568cbe83ee6c9af193585c"
integrity sha512-fEm3Uzw7Mc9Xi//qU20cBKatTfs2aOtKqmvy/Vm7RkJEGFQ4xc9myCfbXxqK//ZS8MR/ciOHw6meGASJuKmDfQ==
Expand Down Expand Up @@ -137,7 +137,7 @@
"@babel/traverse" "^7.10.4"
"@babel/types" "^7.10.4"

"@babel/helper-function-name@^7.10.4", "@babel/helper-function-name@^7.8.3":
"@babel/helper-function-name@^7.10.4":
version "7.10.4"
resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.10.4.tgz#d2d3b20c59ad8c47112fa7d2a94bc09d5ef82f1a"
integrity sha512-YdaSyz1n8gY44EmN7x44zBn9zQ1Ry2Y+3GTA+3vH6Mizke1Vw0aWDM66FOYEPw8//qKkmqOckrGgTYa+6sceqQ==
Expand All @@ -146,7 +146,7 @@
"@babel/template" "^7.10.4"
"@babel/types" "^7.10.4"

"@babel/helper-get-function-arity@^7.10.4", "@babel/helper-get-function-arity@^7.8.3":
"@babel/helper-get-function-arity@^7.10.4":
version "7.10.4"
resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.10.4.tgz#98c1cbea0e2332f33f9a4661b8ce1505b2c19ba2"
integrity sha512-EkN3YDB+SRDgiIUnNgcmiD361ti+AVbL3f3Henf6dqqUyr5dMsorno0lJWJuLhDhkI5sYEpgj6y9kB8AOU1I2A==
Expand Down Expand Up @@ -242,7 +242,7 @@
dependencies:
"@babel/types" "^7.11.0"

"@babel/helper-split-export-declaration@^7.10.4", "@babel/helper-split-export-declaration@^7.11.0", "@babel/helper-split-export-declaration@^7.8.3":
"@babel/helper-split-export-declaration@^7.10.4", "@babel/helper-split-export-declaration@^7.11.0":
version "7.11.0"
resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.11.0.tgz#f8a491244acf6a676158ac42072911ba83ad099f"
integrity sha512-74Vejvp6mHkGE+m+k5vHY93FX2cAtrw1zXrZXRlG4l410Nm9PxfEiVTn1PjDPV5SnmieiueY4AFg2xqhNFuuZg==
Expand All @@ -264,7 +264,7 @@
"@babel/traverse" "^7.10.4"
"@babel/types" "^7.10.4"

"@babel/helpers@^7.10.4", "@babel/helpers@^7.8.4":
"@babel/helpers@^7.10.4":
version "7.10.4"
resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.10.4.tgz#2abeb0d721aff7c0a97376b9e1f6f65d7a475044"
integrity sha512-L2gX/XeUONeEbI78dXSrJzGdz4GQ+ZTA/aazfUsFaWjSe95kiCuOZ5HsXvkiw3iwF+mFHSRUfJU8t6YavocdXA==
Expand All @@ -273,7 +273,7 @@
"@babel/traverse" "^7.10.4"
"@babel/types" "^7.10.4"

"@babel/highlight@^7.0.0", "@babel/highlight@^7.10.4", "@babel/highlight@^7.8.3":
"@babel/highlight@^7.0.0", "@babel/highlight@^7.10.4":
version "7.10.4"
resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.10.4.tgz#7d1bdfd65753538fabe6c38596cdb76d9ac60143"
integrity sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==
Expand All @@ -282,7 +282,7 @@
chalk "^2.0.0"
js-tokens "^4.0.0"

"@babel/parser@^7.10.4", "@babel/parser@^7.11.0", "@babel/parser@^7.11.1", "@babel/parser@^7.8.3", "@babel/parser@^7.8.4":
"@babel/parser@^7.10.4", "@babel/parser@^7.11.0", "@babel/parser@^7.11.1":
version "7.11.3"
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.11.3.tgz#9e1eae46738bcd08e23e867bab43e7b95299a8f9"
integrity sha512-REo8xv7+sDxkKvoxEywIdsNFiZLybwdI7hcT5uEPyQrSMB4YQ973BfC9OOrD/81MaIjh6UxdulIQXkjmiH3PcA==
Expand Down Expand Up @@ -910,7 +910,7 @@
dependencies:
regenerator-runtime "^0.13.4"

"@babel/template@^7.10.4", "@babel/template@^7.8.3":
"@babel/template@^7.10.4":
version "7.10.4"
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.10.4.tgz#3251996c4200ebc71d1a8fc405fba940f36ba278"
integrity sha512-ZCjD27cGJFUB6nmCB1Enki3r+L5kJveX9pq1SvAUKoICy6CZ9yD8xO086YXdYhvNjBdnekm4ZnaP5yC8Cs/1tA==
Expand All @@ -919,7 +919,7 @@
"@babel/parser" "^7.10.4"
"@babel/types" "^7.10.4"

"@babel/traverse@^7.10.4", "@babel/traverse@^7.11.0", "@babel/traverse@^7.8.4", "@babel/traverse@^7.9.0":
"@babel/traverse@^7.10.4", "@babel/traverse@^7.11.0", "@babel/traverse@^7.9.0":
version "7.11.0"
resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.11.0.tgz#9b996ce1b98f53f7c3e4175115605d56ed07dd24"
integrity sha512-ZB2V+LskoWKNpMq6E5UUCrjtDUh5IOTAyIl0dTjIEoXum/iKWkoIEKIRDnUucO6f+2FzNkE0oD4RLKoPIufDtg==
Expand All @@ -934,7 +934,7 @@
globals "^11.1.0"
lodash "^4.17.19"

"@babel/types@^7.10.4", "@babel/types@^7.10.5", "@babel/types@^7.11.0", "@babel/types@^7.4.4", "@babel/types@^7.8.3", "@babel/types@^7.9.0":
"@babel/types@^7.10.4", "@babel/types@^7.10.5", "@babel/types@^7.11.0", "@babel/types@^7.4.4", "@babel/types@^7.9.0":
version "7.11.0"
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.11.0.tgz#2ae6bf1ba9ae8c3c43824e5861269871b206e90d"
integrity sha512-O53yME4ZZI0jO1EVGtF1ePGl0LHirG4P1ibcD80XyzZcKhcMFeCXmh4Xb1ifGBIV233Qg12x4rBfQgA+tmOukA==
Expand Down Expand Up @@ -4676,7 +4676,7 @@ json5@^1.0.1:
dependencies:
minimist "^1.2.0"

json5@^2.1.0, json5@^2.1.2:
json5@^2.1.2:
version "2.1.3"
resolved "https://registry.yarnpkg.com/json5/-/json5-2.1.3.tgz#c9b0f7fa9233bfe5807fe66fcf3a5617ed597d43"
integrity sha512-KXPvOm8K9IJKFM0bmdn8QXh7udDh1g/giieX0NLCaMnb4hEiVFqnop2ImTXCc5e0/oHz3LTqmHGtExn5hfMkOA==
Expand Down Expand Up @@ -4957,7 +4957,7 @@ lodash.uniq@^4.5.0:
resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773"
integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=

lodash@^4.17.12, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@~4.17.10:
lodash@^4.17.12, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@~4.17.10:
version "4.17.19"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.19.tgz#e48ddedbe30b3321783c5b4301fbd353bc1e4a4b"
integrity sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==
Expand Down Expand Up @@ -6760,7 +6760,7 @@ regenerate@^1.4.0:
resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.0.tgz#4a856ec4b56e4077c557589cae85e7a4c8869a11"
integrity sha512-1G6jJVDWrt0rK99kBjvEtziZNCICAuvIPkSiUFIQxVP06RCVpq3dmDo2oi6ABpYaDYaTRr67BEhL8r1wgEZZKg==

regenerator-runtime@^0.13.2, regenerator-runtime@^0.13.4:
regenerator-runtime@^0.13.4:
version "0.13.7"
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz#cac2dacc8a1ea675feaabaeb8ae833898ae46f55"
integrity sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==
Expand Down