From bc6165bbfefec1822fdf77c442c683e1c9a26299 Mon Sep 17 00:00:00 2001 From: Michel Weststrate Date: Wed, 8 Apr 2020 13:03:41 -0700 Subject: [PATCH] Measure how many bytes are received per plugin Summary: Measure how many byte we receive per plugin, and add this to the plugin stats that are collected Will add a graph to the flipper dashboard, and probably a small visualization in a next diff as well. Reviewed By: priteshrnandgaonkar Differential Revision: D20917583 fbshipit-source-id: bb341531ecf8492080af82c56e73c0ec608f7b36 --- desktop/app/src/Client.tsx | 8 ++++++ desktop/app/src/dispatcher/tracking.tsx | 22 +++++++++++++++ desktop/app/src/utils/messageQueue.tsx | 37 ++++++++++++++++++++----- 3 files changed, 60 insertions(+), 7 deletions(-) diff --git a/desktop/app/src/Client.tsx b/desktop/app/src/Client.tsx index 26be4d3bf..9a9a30c71 100644 --- a/desktop/app/src/Client.tsx +++ b/desktop/app/src/Client.tsx @@ -27,6 +27,7 @@ import {flipperRecorderAddEvent} from './utils/pluginStateRecorder'; import {getPluginKey} from './utils/pluginUtils'; import {processMessageLater} from './utils/messageQueue'; import {sideEffect} from './utils/sideEffect'; +import {emitBytesReceived} from './dispatcher/tracking'; type Plugins = Array; @@ -321,6 +322,8 @@ export default class Client extends EventEmitter { } else if (method === 'execute') { invariant(data.params, 'expected params'); const params: Params = data.params; + const bytes = msg.length * 2; // string lengths are measured in UTF-16 units (not characters), so 2 bytes per char + emitBytesReceived(params.api, bytes); const persistingPlugin: | typeof FlipperPlugin @@ -459,10 +462,15 @@ export default class Client extends EventEmitter { if (!fromPlugin || this.isAcceptingMessagesFromPlugin(plugin)) { const logEventName = this.getLogEventName(data); this.logger.trackTimeSince(mark, logEventName); + emitBytesReceived( + plugin || 'unknown', + payload.data.length * 2, + ); const response: { success?: Object; error?: ErrorType; } = JSON.parse(payload.data); + this.onResponse(response, resolve, reject); } }, diff --git a/desktop/app/src/dispatcher/tracking.tsx b/desktop/app/src/dispatcher/tracking.tsx index f80046e38..678ad361a 100644 --- a/desktop/app/src/dispatcher/tracking.tsx +++ b/desktop/app/src/dispatcher/tracking.tsx @@ -42,6 +42,28 @@ export type UsageSummary = { export const fpsEmitter = new EventEmitter(); +// var is fine, let doesn't have the correct hoisting semantics +// eslint-disable-next-line no-var +var bytesReceivedEmitter: EventEmitter; + +export function onBytesReceived( + callback: (plugin: string, bytes: number) => void, +): () => void { + if (!bytesReceivedEmitter) { + bytesReceivedEmitter = new EventEmitter(); + } + bytesReceivedEmitter.on('bytesReceived', callback); + return () => { + bytesReceivedEmitter.off('bytesReceived', callback); + }; +} + +export function emitBytesReceived(plugin: string, bytes: number) { + if (bytesReceivedEmitter) { + bytesReceivedEmitter.emit('bytesReceived', plugin, bytes); + } +} + export default (store: Store, logger: Logger) => { let droppedFrames: number = 0; let largeFrameDrops: number = 0; diff --git a/desktop/app/src/utils/messageQueue.tsx b/desktop/app/src/utils/messageQueue.tsx index 9d7260ff0..d04e01e74 100644 --- a/desktop/app/src/utils/messageQueue.tsx +++ b/desktop/app/src/utils/messageQueue.tsx @@ -19,6 +19,7 @@ import { import {Idler, BaseIdler} from './Idler'; import {pluginIsStarred, getSelectedPluginKey} from '../reducers/connections'; import {deconstructPluginKey} from './clientUtils'; +import {onBytesReceived} from '../dispatcher/tracking'; const MAX_BACKGROUND_TASK_TIME = 25; @@ -28,6 +29,8 @@ type StatEntry = { messageCountTotal: number; // amount of message received for this plugin messageCountDelta: number; // amout of messages received since previous tracking tick maxTime: number; // maximum time spend in a single reducer call + bytesReceivedTotal: number; // Bytes received + bytesReceivedDelta: number; // Bytes received since last tick }; const pluginBackgroundStats = new Map(); @@ -36,9 +39,19 @@ export function resetPluginBackgroundStatsDelta() { pluginBackgroundStats.forEach((stat) => { stat.cpuTimeDelta = 0; stat.messageCountDelta = 0; + stat.bytesReceivedDelta = 0; }); } +onBytesReceived((plugin: string, bytes: number) => { + if (!pluginBackgroundStats.has(plugin)) { + pluginBackgroundStats.set(plugin, createEmptyStat()); + } + const stat = pluginBackgroundStats.get(plugin)!; + stat.bytesReceivedTotal += bytes; + stat.bytesReceivedDelta += bytes; +}); + export function getPluginBackgroundStats(): { cpuTime: number; // amount of ms cpu used since the last stats (typically every minute) byPlugin: {[plugin: string]: StatEntry}; @@ -71,6 +84,8 @@ if (window) { messageCountDelta, messageCountTotal, maxTime, + bytesReceivedTotal, + bytesReceivedDelta, }, ]) => ({ plugin, @@ -79,21 +94,29 @@ if (window) { cpuTimeDelta, messageCountDelta, maxTime, + bytesReceivedTotal, + bytesReceivedDelta, }), ), ); }; } +function createEmptyStat(): StatEntry { + return { + cpuTimeDelta: 0, + cpuTimeTotal: 0, + messageCountDelta: 0, + messageCountTotal: 0, + maxTime: 0, + bytesReceivedTotal: 0, + bytesReceivedDelta: 0, + }; +} + function addBackgroundStat(plugin: string, cpuTime: number) { if (!pluginBackgroundStats.has(plugin)) { - pluginBackgroundStats.set(plugin, { - cpuTimeDelta: 0, - cpuTimeTotal: 0, - messageCountDelta: 0, - messageCountTotal: 0, - maxTime: 0, - }); + pluginBackgroundStats.set(plugin, createEmptyStat()); } const stat = pluginBackgroundStats.get(plugin)!; stat.cpuTimeDelta += cpuTime;