Skip to content

Commit b5d88d5

Browse files
lilysjtu2011copybara-github
authored andcommitted
[XProf: trace viewer] Implement Feature Flag system with Local Storage
This CL adds a simple feature flag system to XProf Trace Viewer v2. It uses `localStorage` as the source of truth in TypeScript to allow users to enable/disable experimental features. The flags are exposed to the WebAssembly (C++) application via Emscripten bindings. C++ queries the flags on demand and caches them to avoid repeated calls to JavaScript, ensuring performance. PiperOrigin-RevId: 914134151
1 parent 46b35aa commit b5d88d5

4 files changed

Lines changed: 46 additions & 1 deletion

File tree

frontend/app/components/trace_viewer_v2/application.cc

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,13 @@ const char* const kWindowTarget = EMSCRIPTEN_EVENT_TARGET_WINDOW;
4141
const char* const kCanvasTarget = "#canvas";
4242
constexpr float kScrollbarSize = 10.0f;
4343

44+
EM_JS(bool, GetFeatureFlagFromJS, (const char* name), {
45+
if (window.getFeatureFlag) {
46+
return window.getFeatureFlag(UTF8ToString(name));
47+
}
48+
return false;
49+
});
50+
4451
EM_BOOL OnResize(int eventType, const EmscriptenUiEvent* uiEvent,
4552
void* userData) {
4653
Application::Instance().RequestRedraw();
@@ -325,6 +332,17 @@ void Application::SetVisibleFlowCategories(
325332
emscripten::vecFromJSArray<int>(category_ids));
326333
}
327334

335+
bool Application::IsFeatureEnabled(const std::string& name) {
336+
auto it = feature_flags_cache_.find(name);
337+
if (it != feature_flags_cache_.end()) {
338+
return it->second;
339+
}
340+
341+
bool enabled = GetFeatureFlagFromJS(name.c_str());
342+
feature_flags_cache_[name] = enabled;
343+
return enabled;
344+
}
345+
328346
void Application::Resize(float dpr, int width, int height) {
329347
float old_dpr = CanvasState::Current().device_pixel_ratio();
330348

frontend/app/components/trace_viewer_v2/application.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,21 @@
55
#include <cstdint>
66
#include <memory>
77
#include <string>
8+
#include <unordered_map>
89

910
#include "emscripten/val.h"
1011
#include "absl/base/no_destructor.h"
1112
#include "absl/time/clock.h"
1213
#include "absl/time/time.h"
1314
#include "imgui.h"
1415
#include "frontend/app/components/trace_viewer_v2/canvas_state.h"
16+
#include "frontend/app/components/trace_viewer_v2/color/colors.h"
1517
#include "frontend/app/components/trace_viewer_v2/scheduler.h"
1618
#include "frontend/app/components/trace_viewer_v2/timeline/data_provider.h"
1719
#include "frontend/app/components/trace_viewer_v2/timeline/time_range.h"
1820
#include "frontend/app/components/trace_viewer_v2/timeline/timeline.h"
1921
#include "frontend/app/components/trace_viewer_v2/trace_helper/trace_event.h"
2022
#include "frontend/app/components/trace_viewer_v2/webgpu_render_platform.h"
21-
#include "frontend/app/components/trace_viewer_v2/color/colors.h"
2223

2324
namespace traceviewer {
2425

@@ -52,6 +53,8 @@ class Application {
5253

5354
ColorPalette& GetPalette() { return palette_; };
5455

56+
bool IsFeatureEnabled(const std::string& name);
57+
5558
bool IsInitialized() const { return timeline_ != nullptr; }
5659

5760
void SetVisibleFlowCategory(int category_id) {
@@ -120,6 +123,8 @@ class Application {
120123
ColorPalette palette_ = ColorPalette::Default();
121124
uint64_t palette_version_ = 0xDEADBEEF;
122125

126+
std::unordered_map<std::string, bool> feature_flags_cache_;
127+
123128
void MainLoop();
124129
// Draws a single frame. Should ONLY be called from MainLoop() to ensure
125130
// DeltaTime and Animations are updated correctly. Other methods should

frontend/app/components/trace_viewer_v2/application_test.cc

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#include "frontend/app/components/trace_viewer_v2/application.h"
22

33
#include <emscripten/bind.h>
4+
#include <emscripten/em_asm.h>
45
#include <emscripten/emscripten.h>
56

67
#include "<gtest/gtest.h>"
@@ -55,5 +56,19 @@ TEST_F(ApplicationTest, ShutdownClearsImGuiContext) {
5556
EXPECT_FALSE(app.IsInitialized());
5657
}
5758

59+
TEST_F(ApplicationTest, IsFeatureEnabledReadsFromJs) {
60+
EM_ASM({
61+
window.getFeatureFlag = (name) => {
62+
if (name === 'test_flag_true') return true;
63+
if (name === 'test_flag_false') return false;
64+
return false;
65+
};
66+
});
67+
Application& app = Application::Instance();
68+
69+
EXPECT_TRUE(app.IsFeatureEnabled("test_flag_true"));
70+
EXPECT_FALSE(app.IsFeatureEnabled("test_flag_false"));
71+
}
72+
5873
} // namespace
5974
} // namespace traceviewer

frontend/app/components/trace_viewer_v2/main.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,7 @@ declare global {
137137

138138
export declare interface TraceViewerV2Module extends EmscriptenModule {
139139
HEAPU8: Uint8Array;
140+
getFeatureFlag?(name: string): boolean;
140141
SetPalette(paletteName: string): void;
141142
canvas: HTMLCanvasElement;
142143
callMain(args: string[]): void;
@@ -940,6 +941,12 @@ export async function traceViewerV2Main(
940941

941942
try {
942943
traceviewerModule = await initGpuAndStartWasmApp();
944+
traceviewerModule.getFeatureFlag = (name: string): boolean => {
945+
// TODO(b/498744795): Now only supports boolean flags (true/false).
946+
// Will be extended in the future.
947+
const value = window.localStorage.getItem(`xprof_ff_${name}`);
948+
return value === 'true';
949+
};
943950
activeWasmModule = traceviewerModule;
944951
} catch (e) {
945952
const error = e as Error;

0 commit comments

Comments
 (0)