Skip to content

Commit

Permalink
feat(report): make RN and Flutter threads stand out nicely (#205)
Browse files Browse the repository at this point in the history
* chore(measure): fix local development

* fix(measure): fix unique key issue

* fix(measure): fix apex to version with good performance

* fix(measure): use proper flutter thread name

* refactor: refactor thread names

* feat(report): autoselect new threads

* chore(report): remove parnthesis in example reports

* ui(report): allow for React component in Chart title

This removes the toolbar on the right side to download chart as png, svg.. but I don't think this was widely used

* feat(report): make RN/Flutter threads stand out nicely
  • Loading branch information
Almouro committed Mar 5, 2024
1 parent 3aeb056 commit 0e99a27
Show file tree
Hide file tree
Showing 26 changed files with 580 additions and 144 deletions.
2 changes: 1 addition & 1 deletion examples/cli/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ const measures: Measure[] = [];
const polling = profiler.pollPerformanceMeasures(bundleId, {
onMeasure: (measure: Measure) => {
measures.push(measure);
console.log(`JS Thread CPU Usage: ${measure.cpu.perName[ThreadNames.JS_THREAD]}%`);
console.log(`JS Thread CPU Usage: ${measure.cpu.perName[ThreadNames.RN.JS_ANDROID]}%`);
console.log(`RAM Usage: ${measure.ram}MB`);
},
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ High CPU Usage
0.5 s
Impacted threads:
-
mqt_js
RN JS Thread
for
0.5
Expand All @@ -41,12 +41,15 @@ It’s worth noting that results might be higher than expected since we measure
here for more details
)
Threads
Frame rate (FPS)
Total CPU Usage (%)
RN JS Thread CPU Usage (%)
Other threads
Thread
Average CPU Usage (%)
sorted descending
Current CPU Usage (%)
mqt_js
RN JS Thread
50
0
com.example
Expand All @@ -67,7 +70,7 @@ OkHttp Dispatch
mali-cmar-backe
1
0
mqt_native_modu
RN Bridge Thread
1
0
Signal Catcher
Expand Down Expand Up @@ -174,7 +177,8 @@ Binder #5
0
JITWorker
0
0"
0
RAM Usage (MB)"
`;

exports[`flashlight measure interactive it displays measures: Web app with measures and threads opened - 2. FULL 1`] = `
Expand Down Expand Up @@ -644,7 +648,7 @@ exports[`flashlight measure interactive it displays measures: Web app with measu
<p
class="whitespace-pre"
>
- mqt_js for 0.5s
- RN JS Thread for 0.5s
</p>
</div>
High CPU usage by a single process can cause app unresponsiveness, even with low overall CPU usage. For instance, an overworked JS thread in a React Native app may lead to unresponsiveness despite maintaining 60 FPS.
Expand Down Expand Up @@ -735,6 +739,11 @@ exports[`flashlight measure interactive it displays measures: Web app with measu
<div
class="mx-8 p-6 bg-dark-charcoal border border-gray-800 rounded-lg"
>
<div
class="mb-[5px] ml-[10px] text-2xl text-white flex flex-row font-medium"
>
Frame rate (FPS)
</div>
<apex-charts
height="500"
options="[object Object]"
Expand All @@ -748,12 +757,60 @@ exports[`flashlight measure interactive it displays measures: Web app with measu
<div
class="mx-8 p-6 bg-dark-charcoal border border-gray-800 rounded-lg"
>
<div
class="mb-[5px] ml-[10px] text-2xl text-white flex flex-row font-medium"
>
Total CPU Usage (%)
</div>
<apex-charts
height="500"
options="[object Object]"
series="[object Object]"
type="line"
/>
<div
class="mb-[5px] ml-[10px] text-2xl text-white flex flex-row font-medium"
>
<div
class="flex flex-row align-center"
>
<div
class="mr-3 flex items-center"
>
<svg
height="30"
viewBox="-11.5 -10.23174 23 20.46348"
width="30"
xmlns="http://www.w3.org/2000/svg"
>
<circle
fill="#61dafb"
r="2.05"
/>
<g
fill="none"
stroke="#61dafb"
>
<ellipse
rx="11"
ry="4.2"
/>
<ellipse
rx="11"
ry="4.2"
transform="matrix(.5 .8660254 -.8660254 .5 0 0)"
/>
<ellipse
rx="11"
ry="4.2"
transform="matrix(-.5 .8660254 -.8660254 -.5 0 0)"
/>
</g>
</svg>
</div>
RN JS Thread CPU Usage (%)
</div>
</div>
<apex-charts
height="500"
options="[object Object]"
Expand All @@ -772,7 +829,7 @@ exports[`flashlight measure interactive it displays measures: Web app with measu
<div
class="text-neutral-200 text-xl"
>
Threads
Other threads
</div>
</div>
<svg
Expand Down Expand Up @@ -946,7 +1003,7 @@ exports[`flashlight measure interactive it displays measures: Web app with measu
id="enhanced-table-checkbox-0"
scope="row"
>
mqt_js
RN JS Thread
</th>
<td
class="MuiTableCell-root MuiTableCell-body MuiTableCell-alignRight MuiTableCell-sizeSmall !text-neutral-300 border-b-neutral-500 css-tqm498-MuiTableCell-root"
Expand Down Expand Up @@ -1310,7 +1367,7 @@ exports[`flashlight measure interactive it displays measures: Web app with measu
id="enhanced-table-checkbox-7"
scope="row"
>
mqt_native_modu
RN Bridge Thread
</th>
<td
class="MuiTableCell-root MuiTableCell-body MuiTableCell-alignRight MuiTableCell-sizeSmall !text-neutral-300 border-b-neutral-500 css-tqm498-MuiTableCell-root"
Expand Down Expand Up @@ -3155,6 +3212,11 @@ exports[`flashlight measure interactive it displays measures: Web app with measu
<div
class="mx-8 p-6 bg-dark-charcoal border border-gray-800 rounded-lg"
>
<div
class="mb-[5px] ml-[10px] text-2xl text-white flex flex-row font-medium"
>
RAM Usage (MB)
</div>
<apex-charts
height="500"
options="[object Object]"
Expand Down
4 changes: 2 additions & 2 deletions packages/commands/measure/src/__tests__/measure.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -100,8 +100,8 @@ describe("flashlight measure interactive", () => {
await screen.findByText("47");

// expand threads
await screen.findByText("Threads");
fireEvent.click(screen.getByText("Threads"));
await screen.findByText("Other threads");
fireEvent.click(screen.getByText("Other threads"));

expectWebAppToMatchSnapshot("Web app with measures and threads opened");

Expand Down
17 changes: 11 additions & 6 deletions packages/commands/measure/src/__tests__/server/ServerApp.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import express from "express";
import fs from "fs";

import { createExpressApp } from "../../server/ServerApp";
import type { FlashlightData } from "../../common/types";

jest.mock("fs", () => ({
promises: {
Expand All @@ -13,32 +12,38 @@ jest.mock("fs", () => ({

describe("ServerApp", () => {
let app: express.Express;
const injected: FlashlightData = { socketServerUrl: "http://localhost:9999" };

beforeAll(() => {
jest.spyOn(express, "static").mockImplementation(() => (req, res, next) => next());
});

const FLASHLIGHT_DATA_PLACEHOLDER =
'window.__FLASHLIGHT_DATA__ = { socketServerUrl: "http://localhost:3000" };';

beforeEach(() => {
(fs.promises.readFile as jest.Mock).mockResolvedValue(
"<html><script>__FLASHLIGHT_DATA__;</script></html>"
`<html><script>${FLASHLIGHT_DATA_PLACEHOLDER}</script></html>`
);

app = createExpressApp(injected);
app = createExpressApp({
port: 9999,
});
});

describe("GET /", () => {
it("injects FlashlightData into index.html", async () => {
const response = await supertest(app).get("/");

expect(response.statusCode).toBe(200);
expect(response.text).toContain(`window.__FLASHLIGHT_DATA__ = ${JSON.stringify(injected)};`);
expect(response.text).toContain(
`window.__FLASHLIGHT_DATA__ = { socketServerUrl: "http://localhost:9999" };`
);
});
});

test("index.html contains the FlashlightData placeholder", async () => {
const fsPromises = jest.requireActual("fs").promises;
const fileContent = await fsPromises.readFile(`${__dirname}/../../webapp/index.html`, "utf8");
expect(fileContent).toContain("__FLASHLIGHT_DATA__;");
expect(fileContent).toContain(FLASHLIGHT_DATA_PLACEHOLDER);
});
});
11 changes: 4 additions & 7 deletions packages/commands/measure/src/server/ServerApp.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,22 +12,19 @@ import { getWebAppUrl } from "./constants";
import { ServerSocketConnectionApp } from "./ServerSocketConnectionApp";
import { useInput } from "ink";
import { profiler } from "@perf-profiler/profiler";
import type { FlashlightData } from "../common/types";

const pathToDist = path.join(__dirname, "../../dist");

export const createExpressApp = (injected: FlashlightData) => {
export const createExpressApp = ({ port }: { port: number }) => {
const app = express();
app.use(cors({ origin: true }));

app.get("/", async (_, res) => {
try {
const indexHtml = path.join(pathToDist, "index.html");
let data = await fs.readFile(indexHtml, "utf8");
data = data.replace(
"__FLASHLIGHT_DATA__;",
`window.__FLASHLIGHT_DATA__ = ${JSON.stringify(injected)};`
);
data = data.replace("localhost:3000", `localhost:${port}`);

res.send(data);
} catch (err) {
res.status(500).send("Error loading the page");
Expand Down Expand Up @@ -67,7 +64,7 @@ export const ServerApp = ({ port }: ServerAppProps) => {
const [socket, setSocket] = useState<SocketType | null>(null);
const webAppUrl = getWebAppUrl(port);
useEffect(() => {
const app = createExpressApp({ socketServerUrl: webAppUrl });
const app = createExpressApp({ port });

const server = http.createServer(app);
const io: SocketServer = new Server(server, {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,12 @@ export const ServerSocketConnectionApp = ({ socket, url }: { socket: SocketType;
const updateMeasures = (measures: Measure[]) =>
setState((state) => updateMeasuresReducer(state, measures));
const addNewResult = (bundleId: string) =>
setState((state) => addNewResultReducer(state, bundleId));
setState((state) =>
addNewResultReducer(
state,
`${bundleId}${state.results.length > 0 ? ` (${state.results.length + 1})` : ""}`
)
);

socket.on("start", async () => {
setState({
Expand Down
4 changes: 2 additions & 2 deletions packages/commands/measure/src/server/socket/socketState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,12 +46,12 @@ export const updateMeasuresReducer = (state: SocketData, measures: Measure[]): S
],
});

export const addNewResultReducer = (state: SocketData, bundleId: string): SocketData => ({
export const addNewResultReducer = (state: SocketData, name: string): SocketData => ({
...state,
results: [
...state.results,
{
name: bundleId,
name,
iterations: [],
status: "SUCCESS",
},
Expand Down
2 changes: 1 addition & 1 deletion packages/commands/measure/src/webapp/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
<div id="app"></div>
<script type="module" src="index.js"></script>
<script>
__FLASHLIGHT_DATA__;
window.__FLASHLIGHT_DATA__ = { socketServerUrl: "http://localhost:3000" };
</script>
</body>
</html>
2 changes: 1 addition & 1 deletion packages/commands/report/src/example-reports/results1.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion packages/commands/report/src/example-reports/results2.json

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

24 changes: 17 additions & 7 deletions packages/core/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,13 +54,23 @@ export interface AveragedTestCaseResult {
export const POLLING_INTERVAL = 500;

export const ThreadNames = {
UI_THREAD: "UI Thread",
JS_THREAD: "mqt_js",
};

export const ThreadNamesIOS = {
UI_THREAD: "Main Thread",
JS_THREAD: "com.facebook.react.JavaScript",
ANDROID: {
UI: "UI Thread",
},
IOS: {
UI: "Main Thread",
},
FLUTTER: {
UI: "1.ui",
RASTER: "1.raster",
IO: "1.io",
},
RN: {
JS_ANDROID: "mqt_js",
JS_BRIDGELESS_ANDROID: "mqt_v_js",
OLD_BRIDGE: "mqt_native_modu",
JS_IOS: "com.facebook.react.JavaScript",
},
};

export interface ScreenRecorder {
Expand Down
4 changes: 3 additions & 1 deletion packages/core/web-reporter-ui/ReporterView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { IterationSelector, useIterationSelector } from "./src/components/Iterat
import { VideoSection } from "./src/sections/VideoSection";
import { VideoEnabledContext } from "./videoCurrentTimeContext";
import { HideSectionIfUndefinedValueFound } from "./src/sections/hideSectionForEmptyValue";
import { mapThreadNames } from "./src/sections/threads";

const Padding = styled.div`
height: 10px;
Expand All @@ -27,12 +28,13 @@ const theme = createTheme({
});

const Report = ({
results,
results: rawResults,
additionalMenuOptions,
}: {
results: TestCaseResult[];
additionalMenuOptions?: MenuOption[];
}) => {
const results = mapThreadNames(rawResults);
const reports = useMemo(() => results.map((result) => new ReportModel(result)), [results]);
const minIterationCount = Math.min(...reports.map((report) => report.getIterationCount()));
const iterationSelector = useIterationSelector(minIterationCount);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ describe("<ReporterView />", () => {
);
expect(screen.getAllByLabelText("Score")[0].textContent).toEqual("69");

fireEvent.click(screen.getByText("Threads"));
fireEvent.click(screen.getByText("Other threads"));

expect(getText(baseElement)).toMatchSnapshot();
expect(asFragment()).toMatchSnapshot();
Expand Down

0 comments on commit 0e99a27

Please sign in to comment.