Merge "add will_notify_on_stop to DataSourceParams" into main
diff --git a/BUILD b/BUILD
index 55ad910..bf86ecc 100644
--- a/BUILD
+++ b/BUILD
@@ -6548,3 +6548,8 @@
main = "tools/write_version_header.py",
python_version = "PY3",
)
+
+exports_files(
+ ["ui/src/assets/favicon.png"],
+ visibility = PERFETTO_CONFIG.public_visibility,
+)
diff --git a/BUILD.extras b/BUILD.extras
index 6bb9928..c166ab9 100644
--- a/BUILD.extras
+++ b/BUILD.extras
@@ -88,3 +88,8 @@
main = "tools/write_version_header.py",
python_version = "PY3",
)
+
+exports_files(
+ ["ui/src/assets/favicon.png"],
+ visibility = PERFETTO_CONFIG.public_visibility,
+)
diff --git a/include/perfetto/tracing/track.h b/include/perfetto/tracing/track.h
index f17ffbb..37f02e5 100644
--- a/include/perfetto/tracing/track.h
+++ b/include/perfetto/tracing/track.h
@@ -120,11 +120,7 @@
// Construct a track using |ptr| as identifier within thread-scope.
// Shorthand for `Track::FromPointer(ptr, ThreadTrack::Current())`
// Usage: TRACE_EVENT_BEGIN("...", "...", perfetto::Track::ThreadScoped(this))
- static Track ThreadScoped(
- const void* ptr,
- Track parent = MakeThreadTrack(base::GetThreadId())) {
- return Track::FromPointer(ptr, parent);
- }
+ static Track ThreadScoped(const void* ptr, Track parent = Track());
protected:
constexpr Track(uint64_t uuid_, uint64_t parent_uuid_)
diff --git a/infra/ui.perfetto.dev/README.md b/infra/ui.perfetto.dev/README.md
index 6006a7f..889402e 100644
--- a/infra/ui.perfetto.dev/README.md
+++ b/infra/ui.perfetto.dev/README.md
@@ -22,17 +22,28 @@
Cloud Build invokes the equivalent of:
```bash
-docker run gcr.io/perfetto-ui/perfetto-ui-builder \
- ui/release/builder_entrypoint.sh
+docker run europe-docker.pkg.dev/perfetto-ui/builder/perfetto-ui-builder \
+ /ui_builder_entrypoint.sh
```
-NOTE: the `builder_entrypoint.sh` script is not bundled in the docker container
-and is taken from the HEAD if the checked out repo.
+NOTE: the `ui_builder_entrypoint.sh` script is bundled in the docker container.
+The container needs to be re-built and re-pushed if the script changes.
To update the container:
+Prerequisite:
+Install the Google Cloud SDK from https://dl.google.com/dl/cloudsdk/release/google-cloud-sdk.tar.gz
+
+
```bash
-cd infra/ui.perfetto.dev/builder
-docker build -t gcr.io/perfetto-ui/perfetto-ui-builder .
-docker push gcr.io/perfetto-ui/perfetto-ui-builder .
+# Obtain a temporary token to impersonate the service account as per
+# https://cloud.google.com/artifact-registry/docs/docker/authentication
+# You need to be a member of perfetto-cloud-infra.prod to do this.
+gcloud auth print-access-token \
+ --impersonate-service-account perfetto-ui-dev@perfetto-ui.iam.gserviceaccount.com | docker login \
+ -u oauth2accesstoken \
+ --password-stdin https://europe-docker.pkg.dev
+
+docker build -t europe-docker.pkg.dev/perfetto-ui/builder/perfetto-ui-builder infra/ui.perfetto.dev/builder
+docker push europe-docker.pkg.dev/perfetto-ui/builder/perfetto-ui-builder
```
diff --git a/infra/ui.perfetto.dev/builder/Dockerfile b/infra/ui.perfetto.dev/builder/Dockerfile
index 07944c1..8c726ee 100644
--- a/infra/ui.perfetto.dev/builder/Dockerfile
+++ b/infra/ui.perfetto.dev/builder/Dockerfile
@@ -15,16 +15,14 @@
# The image that builds the Perfetto UI and deploys to GCS.
# See go/perfetto-ui-autopush for docs on how this works end-to-end.
-FROM debian:buster-slim
+FROM debian:bookworm-slim
ENV PATH=/builder/google-cloud-sdk/bin/:$PATH
RUN set -ex; \
export DEBIAN_FRONTEND=noninteractive; \
apt-get update; \
apt-get -y install python3 python3-distutils python3-pip git curl tar tini \
- pkg-config zip libc-dev libgcc-8-dev; \
- update-alternatives --install /usr/bin/python python /usr/bin/python3.7 1; \
- pip3 install --quiet protobuf crcmod; \
+ pkg-config zip libc-dev python3-protobuf python3-crcmod; \
mkdir -p /builder && \
curl -s -o - https://dl.google.com/dl/cloudsdk/release/google-cloud-sdk.tar.gz | tar -zx -C /builder; \
/builder/google-cloud-sdk/install.sh \
@@ -35,6 +33,10 @@
git config --system credential.helper gcloud.sh; \
useradd -d /home/perfetto perfetto; \
apt-get -y autoremove; \
- rm -rf /var/lib/apt/lists/* /usr/share/man/* /usr/share/doc/*;
+ rm -rf /var/lib/apt/lists/* /usr/share/man/* /usr/share/doc/*; \
+ rm -rf /builder/google-cloud-sdk/.install/.backup;
+
+ADD ui_builder_entrypoint.sh /ui_builder_entrypoint.sh
+RUN chmod 755 /ui_builder_entrypoint.sh
ENTRYPOINT [ "tini", "-g", "--" ]
diff --git a/ui/release/builder_entrypoint.sh b/infra/ui.perfetto.dev/builder/ui_builder_entrypoint.sh
similarity index 90%
rename from ui/release/builder_entrypoint.sh
rename to infra/ui.perfetto.dev/builder/ui_builder_entrypoint.sh
index 27340ea..c1f70d3 100755
--- a/ui/release/builder_entrypoint.sh
+++ b/infra/ui.perfetto.dev/builder/ui_builder_entrypoint.sh
@@ -17,8 +17,6 @@
set -exu
-CUR_DUR=$(dirname ${BASH_SOURCE[0]})
-
env
pwd
mount
@@ -30,6 +28,7 @@
# support yet triggering from Gerrit.
cd /workspace/
+mkdir /workspace/tmp
ls -A1 | xargs rm -rf
UPSTREAM="https://android.googlesource.com/platform/external/perfetto.git"
@@ -38,7 +37,8 @@
cd upstream/
# infra/ui.perfetto.dev/cloudbuild_release.yaml sets $1 to the branch
-# name.
+# name when triggering from a release branch. Otherwise $1 is "" when triggering
+# from main.
EXTRA_ARGS=""
if [[ ! -z $1 ]]; then
git checkout $1
@@ -46,6 +46,5 @@
fi
git rev-parse HEAD
-mkdir /workspace/tmp
-python3 -u "$CUR_DUR/build_all_channels.py" \
+python3 -u "ui/release/build_all_channels.py" \
--upload --tmp=/workspace/tmp $EXTRA_ARGS
diff --git a/infra/ui.perfetto.dev/cloudbuild.yaml b/infra/ui.perfetto.dev/cloudbuild.yaml
index b8905f6..8097d1e 100644
--- a/infra/ui.perfetto.dev/cloudbuild.yaml
+++ b/infra/ui.perfetto.dev/cloudbuild.yaml
@@ -1,10 +1,13 @@
# See go/perfetto-ui-autopush for docs on how this works end-to-end.
+# If this file is modified, the inline YAML must be copy-pasted
+# FROM: infra/ui.perfetto.dev/cloudbuild.yaml
+# TO: TWO trigger configs inline YAML in Google Cloud Console > Cloud Build.
steps:
-- name: gcr.io/$PROJECT_ID/perfetto-ui-builder
+- name: europe-docker.pkg.dev/perfetto-ui/builder/perfetto-ui-builder
args:
- - 'ui/release/builder_entrypoint.sh'
+ - /ui_builder_entrypoint.sh
- ''
- # The extra arg above is load baring. The builder_entrypoint.sh
+ # The empty arg above is load bearing. The builder_entrypoint.sh
# script can't handle $1 sometimes being defined (as in
# cloudbuild.yaml) and sometimes not.
diff --git a/infra/ui.perfetto.dev/cloudbuild_release.yaml b/infra/ui.perfetto.dev/cloudbuild_release.yaml
index bcf6995..1dfe79a 100644
--- a/infra/ui.perfetto.dev/cloudbuild_release.yaml
+++ b/infra/ui.perfetto.dev/cloudbuild_release.yaml
@@ -1,8 +1,11 @@
# See go/perfetto-ui-autopush for docs on how this works end-to-end.
+# If this file is modified, the inline YAML must be copy-pasted
+# FROM: infra/ui.perfetto.dev/cloudbuild.yaml
+# TO: TWO trigger configs inline YAML in Google Cloud Console > Cloud Build.
steps:
-- name: gcr.io/$PROJECT_ID/perfetto-ui-builder
+- name: europe-docker.pkg.dev/perfetto-ui/builder/perfetto-ui-builder
args:
- - 'ui/release/builder_entrypoint.sh'
+ - /ui_builder_entrypoint.sh
- $BRANCH_NAME
# Timeout = 30 min (last measured time in Feb 2021: 12 min)
timeout: 1800s
diff --git a/protos/perfetto/metrics/android/startup_metric.proto b/protos/perfetto/metrics/android/startup_metric.proto
index e2ca850..16a0c3e 100644
--- a/protos/perfetto/metrics/android/startup_metric.proto
+++ b/protos/perfetto/metrics/android/startup_metric.proto
@@ -234,6 +234,112 @@
optional string details = 2;
}
+ // Contains information for slow startup causes.
+ message SlowStartReason {
+ // Points to reason description and solution.
+ enum ReasonId {
+ REASON_ID_UNSPECIFIED = 0;
+ NO_BASELINE_OR_CLOUD_PROFILES = 1;
+ RUN_FROM_APK = 2;
+ UNLOCK_RUNNING = 3;
+ APP_IN_DEBUGGABLE_MODE = 4;
+ GC_ACTIVITY = 5;
+ DEX2OAT_RUNNING = 6;
+ INSTALLD_RUNNING = 7;
+ MAIN_THREAD_TIME_SPENT_IN_RUNNABLE = 8;
+ MAIN_THREAD_TIME_SPENT_IN_INTERRUPTIBLE_SLEEP = 9;
+ MAIN_THREAD_TIME_SPENT_IN_BLOCKING_IO = 10;
+ MAIN_THREAD_TIME_SPENT_IN_OPEN_DEX_FILES_FROM_OAT = 11;
+ TIME_SPENT_IN_BIND_APPLICATION = 12;
+ TIME_SPENT_IN_VIEW_INFLATION = 13;
+ TIME_SPENT_IN_RESOURCES_MANAGER_GET_RESOURCES = 14;
+ TIME_SPENT_VERIFYING_CLASSES = 15;
+ POTENTIAL_CPU_CONTENTION_WITH_ANOTHER_PROCESS = 16;
+ JIT_ACTIVITY = 17;
+ MAIN_THREAD_LOCK_CONTENTION = 18;
+ MAIN_THREAD_MONITOR_CONTENTION = 19;
+ JIT_COMPILED_METHODS = 20;
+ BROADCAST_DISPATCHED_COUNT = 21;
+ BROADCAST_RECEIVED_COUNT = 22;
+ STARTUP_RUNNING_CONCURRENT = 23;
+ MAIN_THREAD_BINDER_TRANSCATIONS_BLOCKED = 24;
+ }
+ optional ReasonId reason_id = 1;
+
+ // Brief description for human readability.
+ optional string reason = 2;
+
+ // Expected value (inherited from threshold definition).
+ optional ThresholdValue expected_value = 3;
+
+ // Actual value, can be used to decide severity level.
+ optional ActualValue actual_value = 4;
+
+ // Launch duration
+ optional int64 launch_dur = 5;
+
+ // Sum of durations of slices and thread states in trace_slices_or_threads.
+ // Can be used to decide if a couple of top slices or threads caused the issue.
+ optional int64 duration = 6;
+
+ // Information of a subset of slice and thread sections to focused on,
+ // sorted by the duration in descending order.
+ // By checking out the top slices/threads, developers can identify specific
+ // slices or threads for further investigation.
+ repeated TraceSliceSection trace_slice_sections = 7;
+ repeated TraceThreadSection trace_thread_sections = 8;
+
+ // Details specific for a reason.
+ optional string additional_info = 9;
+ }
+
+ message ThresholdValue {
+ // Expected value. 1 for true and 0 for false for booleans.
+ optional int64 value = 1;
+
+ // Expected value unit. Enum, e.g. "ns", "%"
+ enum ThresholdUnit {
+ THRESHOLD_UNIT_UNSPECIFIED = 0;
+ NS = 1;
+ PERCENTAGE = 2;
+ TRUE_OR_FALSE = 3;
+ }
+ optional ThresholdUnit unit = 2;
+
+ // For numeric threshold values only. When higher_expected is true,
+ // an app startup is considered performant if actual value is higher
+ // than the threshold.
+ optional bool higher_expected = 3;
+ }
+
+ message ActualValue {
+ // Actual value. 1 for true and 0 for false for booleans.
+ optional int64 value = 1;
+
+ // Actual duration for percentage thresholds only.
+ // E.g. if the threashold is 20% and the launch_duration is 1000ms,
+ // then the actual duration is more than 200ms.
+ optional int64 dur = 2;
+ }
+
+ // Contains information for a section of a slice.
+ message TraceSliceSection {
+ optional int64 start_timestamp = 1;
+
+ optional int64 end_timestamp = 2;
+
+ optional uint32 slice_id = 3;
+ }
+
+ // Contains information for a section of a thread.
+ message TraceThreadSection {
+ optional int64 start_timestamp = 1;
+
+ optional int64 end_timestamp = 2;
+
+ optional uint32 thread_utid = 3;
+ }
+
// Next id: 22
message Startup {
// Random id uniquely identifying an app startup in this trace.
diff --git a/protos/perfetto/metrics/perfetto_merged_metrics.proto b/protos/perfetto/metrics/perfetto_merged_metrics.proto
index 8f736b3..1db8a09 100644
--- a/protos/perfetto/metrics/perfetto_merged_metrics.proto
+++ b/protos/perfetto/metrics/perfetto_merged_metrics.proto
@@ -2350,6 +2350,112 @@
optional string details = 2;
}
+ // Contains information for slow startup causes.
+ message SlowStartReason {
+ // Points to reason description and solution.
+ enum ReasonId {
+ REASON_ID_UNSPECIFIED = 0;
+ NO_BASELINE_OR_CLOUD_PROFILES = 1;
+ RUN_FROM_APK = 2;
+ UNLOCK_RUNNING = 3;
+ APP_IN_DEBUGGABLE_MODE = 4;
+ GC_ACTIVITY = 5;
+ DEX2OAT_RUNNING = 6;
+ INSTALLD_RUNNING = 7;
+ MAIN_THREAD_TIME_SPENT_IN_RUNNABLE = 8;
+ MAIN_THREAD_TIME_SPENT_IN_INTERRUPTIBLE_SLEEP = 9;
+ MAIN_THREAD_TIME_SPENT_IN_BLOCKING_IO = 10;
+ MAIN_THREAD_TIME_SPENT_IN_OPEN_DEX_FILES_FROM_OAT = 11;
+ TIME_SPENT_IN_BIND_APPLICATION = 12;
+ TIME_SPENT_IN_VIEW_INFLATION = 13;
+ TIME_SPENT_IN_RESOURCES_MANAGER_GET_RESOURCES = 14;
+ TIME_SPENT_VERIFYING_CLASSES = 15;
+ POTENTIAL_CPU_CONTENTION_WITH_ANOTHER_PROCESS = 16;
+ JIT_ACTIVITY = 17;
+ MAIN_THREAD_LOCK_CONTENTION = 18;
+ MAIN_THREAD_MONITOR_CONTENTION = 19;
+ JIT_COMPILED_METHODS = 20;
+ BROADCAST_DISPATCHED_COUNT = 21;
+ BROADCAST_RECEIVED_COUNT = 22;
+ STARTUP_RUNNING_CONCURRENT = 23;
+ MAIN_THREAD_BINDER_TRANSCATIONS_BLOCKED = 24;
+ }
+ optional ReasonId reason_id = 1;
+
+ // Brief description for human readability.
+ optional string reason = 2;
+
+ // Expected value (inherited from threshold definition).
+ optional ThresholdValue expected_value = 3;
+
+ // Actual value, can be used to decide severity level.
+ optional ActualValue actual_value = 4;
+
+ // Launch duration
+ optional int64 launch_dur = 5;
+
+ // Sum of durations of slices and thread states in trace_slices_or_threads.
+ // Can be used to decide if a couple of top slices or threads caused the issue.
+ optional int64 duration = 6;
+
+ // Information of a subset of slice and thread sections to focused on,
+ // sorted by the duration in descending order.
+ // By checking out the top slices/threads, developers can identify specific
+ // slices or threads for further investigation.
+ repeated TraceSliceSection trace_slice_sections = 7;
+ repeated TraceThreadSection trace_thread_sections = 8;
+
+ // Details specific for a reason.
+ optional string additional_info = 9;
+ }
+
+ message ThresholdValue {
+ // Expected value. 1 for true and 0 for false for booleans.
+ optional int64 value = 1;
+
+ // Expected value unit. Enum, e.g. "ns", "%"
+ enum ThresholdUnit {
+ THRESHOLD_UNIT_UNSPECIFIED = 0;
+ NS = 1;
+ PERCENTAGE = 2;
+ TRUE_OR_FALSE = 3;
+ }
+ optional ThresholdUnit unit = 2;
+
+ // For numeric threshold values only. When higher_expected is true,
+ // an app startup is considered performant if actual value is higher
+ // than the threshold.
+ optional bool higher_expected = 3;
+ }
+
+ message ActualValue {
+ // Actual value. 1 for true and 0 for false for booleans.
+ optional int64 value = 1;
+
+ // Actual duration for percentage thresholds only.
+ // E.g. if the threashold is 20% and the launch_duration is 1000ms,
+ // then the actual duration is more than 200ms.
+ optional int64 dur = 2;
+ }
+
+ // Contains information for a section of a slice.
+ message TraceSliceSection {
+ optional int64 start_timestamp = 1;
+
+ optional int64 end_timestamp = 2;
+
+ optional uint32 slice_id = 3;
+ }
+
+ // Contains information for a section of a thread.
+ message TraceThreadSection {
+ optional int64 start_timestamp = 1;
+
+ optional int64 end_timestamp = 2;
+
+ optional uint32 thread_utid = 3;
+ }
+
// Next id: 22
message Startup {
// Random id uniquely identifying an app startup in this trace.
diff --git a/protos/perfetto/trace/android/BUILD.gn b/protos/perfetto/trace/android/BUILD.gn
index 97e9d52..900a64b 100644
--- a/protos/perfetto/trace/android/BUILD.gn
+++ b/protos/perfetto/trace/android/BUILD.gn
@@ -58,6 +58,10 @@
# Winscope messages added to TracePacket as extensions
perfetto_proto_library("winscope_extensions_@TYPE@") {
+ proto_generators = [
+ "zero",
+ "source_set",
+ ]
public_deps = [ ":winscope_common_@TYPE@" ]
sources = [
"inputmethodeditor.proto",
diff --git a/protos/third_party/chromium/chrome_track_event.proto b/protos/third_party/chromium/chrome_track_event.proto
index 8b766cb..e43f424 100644
--- a/protos/third_party/chromium/chrome_track_event.proto
+++ b/protos/third_party/chromium/chrome_track_event.proto
@@ -1632,6 +1632,7 @@
DEADLINE_MODE_REGULAR = 3;
DEADLINE_MODE_LATE = 4;
DEADLINE_MODE_BLOCKED = 5;
+ DEADLINE_MODE_WAIT_FOR_SCROLL = 6;
}
optional ChromeCompositorStateMachineV2 state_machine = 1;
optional bool observing_begin_frame_source = 2;
@@ -1857,9 +1858,36 @@
optional CallSite call_site = 2;
}
+message AnimationFrameTimingInfo {
+ optional int64 blocking_duration_ms = 1;
+ optional int64 duration_ms = 2;
+ optional int64 num_scripts = 3;
+}
+
+message AnimationFrameScriptTimingInfo {
+ optional int64 style_duration_ms = 1;
+ optional int64 layout_duration_ms = 2;
+ optional int64 pause_duration_ms = 3;
+ optional string class_like_name = 4;
+ optional string property_like_name = 5;
+ optional string source_location_url = 6;
+ optional string source_location_function_name = 7;
+ optional int64 source_location_char_position = 8;
+ enum InvokerType {
+ UNDEFINED = 0;
+ CLASSIC_SCRIPT = 1;
+ MODULE_SCRIPT = 2;
+ USER_CALLBACK = 3;
+ EVENT_HANDLER = 4;
+ PROMISE_RESOLVE = 5;
+ PROMISE_REJECT = 6;
+ }
+ optional InvokerType invoker_type = 9;
+}
+
message ChromeTrackEvent {
// Extension range for Chrome: 1000-1999
- // Next ID: 1064
+ // Next ID: 1066
extend TrackEvent {
optional ChromeAppState chrome_app_state = 1000;
@@ -1994,5 +2022,10 @@
optional ChromeCompositorSchedulerStateV2 cc_scheduler_state = 1062;
optional WebViewStartup webview_startup = 1063;
+
+ optional AnimationFrameTimingInfo animation_frame_timing_info = 1064;
+
+ optional AnimationFrameScriptTimingInfo animation_frame_script_timing_info =
+ 1065;
}
}
diff --git a/python/perfetto/trace_processor/metrics.descriptor b/python/perfetto/trace_processor/metrics.descriptor
index 9c1dfa0..4b307a8 100644
--- a/python/perfetto/trace_processor/metrics.descriptor
+++ b/python/perfetto/trace_processor/metrics.descriptor
Binary files differ
diff --git a/src/trace_processor/importers/proto/profile_module.cc b/src/trace_processor/importers/proto/profile_module.cc
index 6064d33..2bfb78a 100644
--- a/src/trace_processor/importers/proto/profile_module.cc
+++ b/src/trace_processor/importers/proto/profile_module.cc
@@ -408,7 +408,10 @@
src_allocation.heap_name =
context_->storage->InternString(entry.heap_name());
} else {
- src_allocation.heap_name = context_->storage->InternString("malloc");
+ // After aosp/1348782 there should be a heap name associated with all
+ // allocations - absence of one is likely a bug (for traces captured
+ // in older builds, this was the native heap profiler (libc.malloc)).
+ src_allocation.heap_name = context_->storage->InternString("unknown");
}
src_allocation.timestamp = timestamp;
src_allocation.callstack_id = sample.callstack_id();
diff --git a/src/trace_processor/perfetto_sql/stdlib/android/startup/startups_maxsdk28.sql b/src/trace_processor/perfetto_sql/stdlib/android/startup/startups_maxsdk28.sql
index 3faf091..071968a 100644
--- a/src/trace_processor/perfetto_sql/stdlib/android/startup/startups_maxsdk28.sql
+++ b/src/trace_processor/perfetto_sql/stdlib/android/startup/startups_maxsdk28.sql
@@ -37,11 +37,12 @@
sl.ts,
rs.ts + rs.dur AS ts_end,
-- We use the process name as the package as we have no better option.
- process_name AS package,
+ COALESCE(process_name, thread_name, 'unknown') AS package,
"hot" AS startup_type
FROM thread_slice sl
JOIN android_first_frame_after(sl.ts) rs
WHERE name = 'activityResume'
+ AND sl.is_main_thread
-- Remove any launches here where the activityResume slices happens during
-- a warm/cold startup.
AND NOT EXISTS (
diff --git a/src/trace_redaction/collect_frame_cookies.cc b/src/trace_redaction/collect_frame_cookies.cc
index 13e5539..04c5eb3 100644
--- a/src/trace_redaction/collect_frame_cookies.cc
+++ b/src/trace_redaction/collect_frame_cookies.cc
@@ -148,18 +148,12 @@
return base::OkStatus();
}
- const auto* timeline = context->timeline.get();
- auto uid = context->package_uid.value();
-
- auto& package_frame_cookies = context->package_frame_cookies;
-
// Filter the global cookies down to cookies that belong to the target package
// (uid).
for (const auto& cookie : context->global_frame_cookies) {
- auto cookie_slice = timeline->Search(cookie.ts, cookie.pid);
-
- if (cookie_slice.uid == uid) {
- package_frame_cookies.insert(cookie.cookie);
+ if (context->timeline->PidConnectsToUid(cookie.ts, cookie.pid,
+ *context->package_uid)) {
+ context->package_frame_cookies.insert(cookie.cookie);
}
}
diff --git a/src/trace_redaction/collect_timeline_events.cc b/src/trace_redaction/collect_timeline_events.cc
index aac9752..70f92f5 100644
--- a/src/trace_redaction/collect_timeline_events.cc
+++ b/src/trace_redaction/collect_timeline_events.cc
@@ -37,7 +37,7 @@
using TaskNewtaskFtraceEvent = protos::pbzero::TaskNewtaskFtraceEvent;
void MarkOpen(uint64_t ts,
- ProcessTree::Process::Decoder process,
+ const ProcessTree::Process::Decoder& process,
ProcessThreadTimeline* timeline) {
// The uid in the process tree is a int32_t, but in the package list, the uid
// is a uint64_t.
@@ -48,14 +48,14 @@
}
void MarkOpen(uint64_t ts,
- ProcessTree::Thread::Decoder thread,
+ const ProcessTree::Thread::Decoder& thread,
ProcessThreadTimeline* timeline) {
auto e = ProcessThreadTimeline::Event::Open(ts, thread.tid(), thread.tgid());
timeline->Append(e);
}
void MarkClose(const FtraceEvent::Decoder& event,
- SchedProcessFreeFtraceEvent::Decoder process_free,
+ const SchedProcessFreeFtraceEvent::Decoder process_free,
ProcessThreadTimeline* timeline) {
auto e = ProcessThreadTimeline::Event::Close(event.timestamp(),
process_free.pid());
@@ -63,7 +63,7 @@
}
void MarkOpen(const FtraceEvent::Decoder& event,
- TaskNewtaskFtraceEvent::Decoder new_task,
+ const TaskNewtaskFtraceEvent::Decoder new_task,
ProcessThreadTimeline* timeline) {
// Event though pid() is uint32_t. all other pid values use int32_t, so it's
// assumed to be safe to narrow-cast it.
@@ -74,7 +74,7 @@
}
void AppendEvents(uint64_t ts,
- ProcessTree::Decoder tree,
+ const ProcessTree::Decoder& tree,
ProcessThreadTimeline* timeline) {
for (auto it = tree.processes(); it; ++it) {
MarkOpen(ts, ProcessTree::Process::Decoder(*it), timeline);
@@ -85,7 +85,7 @@
}
}
-void AppendEvents(FtraceEventBundle::Decoder ftrace_events,
+void AppendEvents(const FtraceEventBundle::Decoder& ftrace_events,
ProcessThreadTimeline* timeline) {
for (auto it = ftrace_events.event(); it; ++it) {
FtraceEvent::Decoder event(*it);
diff --git a/src/trace_redaction/filter_print_events.cc b/src/trace_redaction/filter_print_events.cc
index 6c1843b..869c7a0 100644
--- a/src/trace_redaction/filter_print_events.cc
+++ b/src/trace_redaction/filter_print_events.cc
@@ -41,9 +41,6 @@
PERFETTO_DCHECK(context.timeline);
PERFETTO_DCHECK(context.package_uid.has_value());
- const auto* timeline = context.timeline.get();
- auto package_uid = context.package_uid;
-
protozero::ProtoDecoder event(bytes);
// This is not a print packet. Keep the packet.
@@ -56,9 +53,9 @@
event.FindField(protos::pbzero::FtraceEvent::kTimestampFieldNumber);
auto pid = event.FindField(protos::pbzero::FtraceEvent::kPidFieldNumber);
- // Pid + Time --> UID, if the uid matches the target package, keep the event.
return pid.valid() && time.valid() &&
- timeline->Search(time.as_uint64(), pid.as_int32()).uid == package_uid;
+ context.timeline->PidConnectsToUid(time.as_uint64(), pid.as_int32(),
+ *context.package_uid);
}
} // namespace perfetto::trace_redaction
diff --git a/src/trace_redaction/filter_sched_waking_events.cc b/src/trace_redaction/filter_sched_waking_events.cc
index e08a9d6..287090d 100644
--- a/src/trace_redaction/filter_sched_waking_events.cc
+++ b/src/trace_redaction/filter_sched_waking_events.cc
@@ -59,14 +59,9 @@
auto outer_pid =
event_decoder.FindField(protos::pbzero::FtraceEvent::kPidFieldNumber);
- if (!outer_pid.valid()) {
- return false; // Remove
- }
-
- auto outer_slice = context.timeline->Search(
- timestamp.as_uint64(), static_cast<int32_t>(outer_pid.as_uint32()));
-
- if (outer_slice.uid != context.package_uid.value()) {
+ if (!outer_pid.valid() ||
+ !context.timeline->PidConnectsToUid(
+ timestamp.as_uint64(), outer_pid.as_int32(), *context.package_uid)) {
return false; // Remove
}
@@ -75,13 +70,13 @@
auto inner_pid = waking_decoder.FindField(
protos::pbzero::SchedWakingFtraceEvent::kPidFieldNumber);
- if (!inner_pid.valid()) {
+ if (!inner_pid.valid() ||
+ !context.timeline->PidConnectsToUid(
+ timestamp.as_uint64(), inner_pid.as_int32(), *context.package_uid)) {
return false; // Remove
}
- auto inner_slice =
- context.timeline->Search(timestamp.as_uint64(), inner_pid.as_int32());
- return inner_slice.uid == context.package_uid.value();
+ return true;
}
} // namespace perfetto::trace_redaction
diff --git a/src/trace_redaction/filter_task_rename.cc b/src/trace_redaction/filter_task_rename.cc
index 65f84fb..467e5b5 100644
--- a/src/trace_redaction/filter_task_rename.cc
+++ b/src/trace_redaction/filter_task_rename.cc
@@ -67,8 +67,8 @@
return false;
}
- auto slice = context.timeline->Search(timestamp.as_uint64(), pid.as_int32());
- return slice.uid == context.package_uid.value();
+ return context.timeline->PidConnectsToUid(
+ timestamp.as_uint64(), pid.as_int32(), *context.package_uid);
}
} // namespace perfetto::trace_redaction
diff --git a/src/trace_redaction/process_thread_timeline.cc b/src/trace_redaction/process_thread_timeline.cc
index c1c3984..43d3637 100644
--- a/src/trace_redaction/process_thread_timeline.cc
+++ b/src/trace_redaction/process_thread_timeline.cc
@@ -45,8 +45,9 @@
mode_ = Mode::kRead;
}
-ProcessThreadTimeline::Slice ProcessThreadTimeline::Search(uint64_t ts,
- int32_t pid) const {
+bool ProcessThreadTimeline::PidConnectsToUid(uint64_t ts,
+ int32_t pid,
+ uint64_t uid) const {
PERFETTO_DCHECK(mode_ == Mode::kRead);
auto event = FindPreviousEvent(ts, pid);
@@ -54,26 +55,23 @@
for (size_t d = 0; d < kMaxSearchDepth; ++d) {
// The thread/process was freed. It won't exist until a new open event.
if (event.type != Event::Type::kOpen) {
- return {pid, Event::kUnknownUid};
+ return false;
}
- // System processes all have uids equal to zero, so everything eventually
- // has a uid. This means that all threads should find a process and a uid.
- // However, if a thread does not have a process (this should not happen)
- // that thread will be treated as invalid.
- if (event.uid != Event::kUnknownUid) {
- return {pid, event.uid};
+ // TODO(vaage): Normalize the uid values.
+ if (event.uid == uid) {
+ return true;
}
- // If there is no parent, there is no reason to keep searching.
+ // If there is no parent, there is no way to keep searching.
if (event.ppid == Event::kUnknownPid) {
- return {pid, Event::kUnknownUid};
+ return false;
}
event = FindPreviousEvent(ts, event.ppid);
}
- return {pid, Event::kUnknownUid};
+ return false;
}
ProcessThreadTimeline::Event ProcessThreadTimeline::FindPreviousEvent(
diff --git a/src/trace_redaction/process_thread_timeline.h b/src/trace_redaction/process_thread_timeline.h
index b176121..fdb70ba 100644
--- a/src/trace_redaction/process_thread_timeline.h
+++ b/src/trace_redaction/process_thread_timeline.h
@@ -82,15 +82,6 @@
uint64_t uid = kUnknownUid;
};
- // The state of a process at a specific point in time.
- struct Slice {
- int32_t pid = -1;
-
- // It is safe to use 0 as the invalid value because that's effectively
- // what happening in the trace.
- uint64_t uid = 0;
- };
-
ProcessThreadTimeline() = default;
ProcessThreadTimeline(const ProcessThreadTimeline&) = delete;
@@ -105,12 +96,8 @@
// subset of events will, on average, be trivally small.
void Sort();
- // Returns a snapshot that contains a process's pid and ppid, but contains the
- // first uid found in its parent-child chain. If a uid cannot be found, uid=0
- // is returned.
- //
- // `Sort()` must be called before this.
- Slice Search(uint64_t ts, int32_t pid) const;
+ // Returns true if a process/thread is connected to a package.
+ bool PidConnectsToUid(uint64_t ts, int32_t pid, uint64_t uid) const;
// Finds the pid's last event before ts.
Event FindPreviousEvent(uint64_t ts, int32_t pid) const;
diff --git a/src/trace_redaction/process_thread_timeline_unittest.cc b/src/trace_redaction/process_thread_timeline_unittest.cc
index 23a9727..4e86a8a 100644
--- a/src/trace_redaction/process_thread_timeline_unittest.cc
+++ b/src/trace_redaction/process_thread_timeline_unittest.cc
@@ -193,35 +193,29 @@
// PID A is directly connected to UID A.
TEST_F(ProcessThreadTimelineIsConnectedTest, DirectPidAndUid) {
- auto slice = timeline_.Search(kTimeB, kPidA);
-
- ASSERT_EQ(slice.pid, kPidA);
- ASSERT_EQ(slice.uid, kUidA);
+ ASSERT_TRUE(timeline_.PidConnectsToUid(kTimeB, kPidA, kUidA));
}
// PID B is indirectly connected to UID A through PID A.
TEST_F(ProcessThreadTimelineIsConnectedTest, IndirectPidAndUid) {
- auto slice = timeline_.Search(kTimeB, kPidB);
+ ASSERT_TRUE(timeline_.PidConnectsToUid(kTimeB, kPidB, kUidA));
+}
- ASSERT_EQ(slice.pid, kPidB);
- ASSERT_EQ(slice.uid, kUidA);
+// UID A and UID C are valid packages. However, PID B is connected to UID A, not
+// UID C.
+TEST_F(ProcessThreadTimelineIsConnectedTest, NotConnectedToOtherUid) {
+ ASSERT_FALSE(timeline_.PidConnectsToUid(kTimeB, kPidB, kUidC));
}
// PID D is not in the timeline, so it shouldn't be connected to anything.
TEST_F(ProcessThreadTimelineIsConnectedTest, MissingPid) {
- auto slice = timeline_.Search(kTimeB, kPidD);
-
- ASSERT_EQ(slice.pid, kPidD);
- ASSERT_EQ(slice.uid, ProcessThreadTimeline::Event::kUnknownUid);
+ ASSERT_FALSE(timeline_.PidConnectsToUid(kTimeB, kPidD, kUidA));
}
// Even through there is a connection between PID A and UID A, the query is too
// soon (events are at TIME B, but the query is at TIME A).
TEST_F(ProcessThreadTimelineIsConnectedTest, PrematureDirectPidAndUid) {
- auto slice = timeline_.Search(kTimeA, kPidA);
-
- ASSERT_EQ(slice.pid, kPidA);
- ASSERT_EQ(slice.uid, ProcessThreadTimeline::Event::kUnknownUid);
+ ASSERT_FALSE(timeline_.PidConnectsToUid(kTimeA, kPidA, kUidA));
}
} // namespace perfetto::trace_redaction
diff --git a/src/trace_redaction/redact_sched_switch.cc b/src/trace_redaction/redact_sched_switch.cc
index 8dca030..83cb36f 100644
--- a/src/trace_redaction/redact_sched_switch.cc
+++ b/src/trace_redaction/redact_sched_switch.cc
@@ -26,11 +26,13 @@
namespace {
-protozero::ConstChars SanitizeCommValue(const Context& context,
- ProcessThreadTimeline::Slice slice,
- protozero::Field field) {
- if (NormalizeUid(slice.uid) == NormalizeUid(context.package_uid.value())) {
- return field.as_string();
+// TODO(vaage): Merge with RedactComm in redact_task_newtask.cc.
+protozero::ConstChars RedactComm(const Context& context,
+ uint64_t ts,
+ int32_t pid,
+ protozero::ConstChars comm) {
+ if (context.timeline->PidConnectsToUid(ts, pid, *context.package_uid)) {
+ return comm;
}
return {};
@@ -105,22 +107,19 @@
// Avoid making the message until we know that we have prev and next pids.
auto sched_switch_message = event_message->set_sched_switch();
- auto prev_slice =
- context.timeline->Search(timestamp.as_uint64(), prev_pid.as_int32());
- auto next_slice =
- context.timeline->Search(timestamp.as_uint64(), next_pid.as_int32());
-
for (auto field = sched_switch_decoder.ReadField(); field.valid();
field = sched_switch_decoder.ReadField()) {
switch (field.id()) {
case protos::pbzero::SchedSwitchFtraceEvent::kNextCommFieldNumber:
sched_switch_message->set_next_comm(
- SanitizeCommValue(context, next_slice, field));
+ RedactComm(context, timestamp.as_uint64(), next_pid.as_int32(),
+ field.as_string()));
break;
case protos::pbzero::SchedSwitchFtraceEvent::kPrevCommFieldNumber:
sched_switch_message->set_prev_comm(
- SanitizeCommValue(context, prev_slice, field));
+ RedactComm(context, timestamp.as_uint64(), prev_pid.as_int32(),
+ field.as_string()));
break;
default:
diff --git a/src/trace_redaction/redact_task_newtask.cc b/src/trace_redaction/redact_task_newtask.cc
index c942612..fb0cfe8 100644
--- a/src/trace_redaction/redact_task_newtask.cc
+++ b/src/trace_redaction/redact_task_newtask.cc
@@ -26,18 +26,19 @@
namespace {
-protozero::ConstChars SanitizeCommValue(const Context& context,
- ProcessThreadTimeline::Slice slice,
- protozero::Field field) {
- if (NormalizeUid(slice.uid) == NormalizeUid(context.package_uid.value())) {
- return field.as_string();
+// TODO(vaage): Merge with RedactComm in redact_sched_switch.cc.
+protozero::ConstChars RedactComm(const Context& context,
+ uint64_t ts,
+ int32_t pid,
+ protozero::ConstChars comm) {
+ if (context.timeline->PidConnectsToUid(ts, pid, *context.package_uid)) {
+ return comm;
}
return {};
}
} // namespace
-
// Redact sched switch trace events in an ftrace event bundle:
//
// event {
@@ -93,8 +94,6 @@
// Avoid making the message until we know that we have prev and next pids.
auto* new_task_message = event_message->set_task_newtask();
- auto slice = context.timeline->Search(timestamp.as_uint64(), pid.as_int32());
-
for (auto field = new_task_decoder.ReadField(); field.valid();
field = new_task_decoder.ReadField()) {
// Perfetto view (ui.perfetto.dev) crashes if the comm value is missing.
@@ -102,7 +101,8 @@
// This appears to work.
if (field.id() ==
protos::pbzero::TaskNewtaskFtraceEvent::kCommFieldNumber) {
- new_task_message->set_comm(SanitizeCommValue(context, slice, field));
+ new_task_message->set_comm(RedactComm(context, timestamp.as_uint64(),
+ pid.as_int32(), field.as_string()));
} else {
proto_util::AppendField(field, new_task_message);
}
diff --git a/src/trace_redaction/remap_scheduling_events.cc b/src/trace_redaction/remap_scheduling_events.cc
index 4817f19..be0973e 100644
--- a/src/trace_redaction/remap_scheduling_events.cc
+++ b/src/trace_redaction/remap_scheduling_events.cc
@@ -32,14 +32,18 @@
PERFETTO_DCHECK(context.package_uid.value());
PERFETTO_DCHECK(cpu < context.synthetic_threads->tids.size());
- auto slice = context.timeline->Search(timestamp, pid);
+ // PID 0 is used for CPU idle. If it was to get re-mapped, threading
+ // information get corrupted.
+ if (pid == 0) {
+ return 0;
+ }
- auto expected_uid = NormalizeUid(slice.uid);
- auto actual_uid = NormalizeUid(context.package_uid.value());
+ if (context.timeline->PidConnectsToUid(timestamp, pid,
+ *context.package_uid)) {
+ return pid;
+ }
- return !pid || expected_uid == actual_uid
- ? pid
- : context.synthetic_threads->tids[cpu];
+ return context.synthetic_threads->tids[cpu];
}
} // namespace
diff --git a/src/trace_redaction/scrub_process_stats.cc b/src/trace_redaction/scrub_process_stats.cc
index 19d4048..78c983a 100644
--- a/src/trace_redaction/scrub_process_stats.cc
+++ b/src/trace_redaction/scrub_process_stats.cc
@@ -57,9 +57,6 @@
PERFETTO_DCHECK(time_field.valid());
auto time = time_field.as_uint64();
- auto* timeline = context.timeline.get();
- auto uid = context.package_uid.value();
-
for (auto packet_field = packet_decoder.ReadField(); packet_field.valid();
packet_field = packet_decoder.ReadField()) {
if (packet_field.id() !=
@@ -83,8 +80,9 @@
protozero::ProtoDecoder process_decoder(process_stats_field.as_bytes());
auto pid = process_decoder.FindField(
protos::pbzero::ProcessStats::Process::kPidFieldNumber);
- keep_field =
- pid.valid() && timeline->Search(time, pid.as_int32()).uid == uid;
+
+ keep_field = context.timeline->PidConnectsToUid(time, pid.as_int32(),
+ *context.package_uid);
} else {
keep_field = true;
}
diff --git a/src/trace_redaction/scrub_process_trees.cc b/src/trace_redaction/scrub_process_trees.cc
index 989d094..a3f1f3b 100644
--- a/src/trace_redaction/scrub_process_trees.cc
+++ b/src/trace_redaction/scrub_process_trees.cc
@@ -43,14 +43,10 @@
return;
}
- auto slice = context.timeline->Search(timestamp.as_uint64(), pid.as_int32());
-
- // Only keep the target process cmdline.
- if (NormalizeUid(slice.uid) != NormalizeUid(context.package_uid.value())) {
- return;
+ if (context.timeline->PidConnectsToUid(timestamp.as_uint64(), pid.as_int32(),
+ *context.package_uid)) {
+ proto_util::AppendField(value, message);
}
-
- proto_util::AppendField(value, message);
}
} // namespace
diff --git a/src/tracing/track.cc b/src/tracing/track.cc
index b7d9795..02e6e03 100644
--- a/src/tracing/track.cc
+++ b/src/tracing/track.cc
@@ -49,6 +49,13 @@
desc->AppendRawProtoBytes(bytes.data(), bytes.size());
}
+// static
+Track Track::ThreadScoped(const void* ptr, Track parent) {
+ if (parent.uuid == 0)
+ return Track::FromPointer(ptr, ThreadTrack::Current());
+ return Track::FromPointer(ptr, parent);
+}
+
protos::gen::TrackDescriptor ProcessTrack::Serialize() const {
auto desc = Track::Serialize();
auto pd = desc.mutable_process();
diff --git a/test/trace_processor/diff_tests/parser/profiling/heap_profile_tracker_twoheaps.textproto b/test/trace_processor/diff_tests/parser/profiling/heap_profile_tracker_twoheaps.textproto
index b5a4aed..d3395a3 100644
--- a/test/trace_processor/diff_tests/parser/profiling/heap_profile_tracker_twoheaps.textproto
+++ b/test/trace_processor/diff_tests/parser/profiling/heap_profile_tracker_twoheaps.textproto
@@ -39,7 +39,7 @@
index: 0
continued: false
process_dumps {
- heap_name: "malloc"
+ heap_name: "libc.malloc"
samples {
callstack_id: 1
self_allocated: 1
diff --git a/test/trace_processor/diff_tests/parser/profiling/tests_heap_graph.py b/test/trace_processor/diff_tests/parser/profiling/tests_heap_graph.py
index 3eead2a..205e9e4 100644
--- a/test/trace_processor/diff_tests/parser/profiling/tests_heap_graph.py
+++ b/test/trace_processor/diff_tests/parser/profiling/tests_heap_graph.py
@@ -361,10 +361,10 @@
""",
out=Csv("""
"id","type","ts","upid","heap_name","callsite_id","count","size"
- 0,"heap_profile_allocation",0,0,"malloc",0,1,1
- 1,"heap_profile_allocation",0,0,"malloc",0,-1,-1
- 2,"heap_profile_allocation",1,0,"malloc",0,1,1
- 3,"heap_profile_allocation",1,0,"malloc",0,-1,-1
+ 0,"heap_profile_allocation",0,0,"unknown",0,1,1
+ 1,"heap_profile_allocation",0,0,"unknown",0,-1,-1
+ 2,"heap_profile_allocation",1,0,"unknown",0,1,1
+ 3,"heap_profile_allocation",1,0,"unknown",0,-1,-1
"""))
def test_heap_profile_tracker_twoheaps(self):
@@ -375,8 +375,8 @@
""",
out=Csv("""
"id","type","ts","upid","heap_name","callsite_id","count","size"
- 0,"heap_profile_allocation",0,0,"malloc",0,1,1
- 1,"heap_profile_allocation",0,0,"malloc",0,-1,-1
+ 0,"heap_profile_allocation",0,0,"libc.malloc",0,1,1
+ 1,"heap_profile_allocation",0,0,"libc.malloc",0,-1,-1
2,"heap_profile_allocation",0,0,"custom",0,1,1
3,"heap_profile_allocation",0,0,"custom",0,-1,-1
"""))
diff --git a/test/trace_processor/diff_tests/parser/profiling/tests_heap_profiling.py b/test/trace_processor/diff_tests/parser/profiling/tests_heap_profiling.py
index 483d045..ebf21c7 100644
--- a/test/trace_processor/diff_tests/parser/profiling/tests_heap_profiling.py
+++ b/test/trace_processor/diff_tests/parser/profiling/tests_heap_profiling.py
@@ -59,8 +59,8 @@
""",
out=Csv("""
"id","type","ts","upid","heap_name","callsite_id","count","size"
- 0,"heap_profile_allocation",-10,2,"malloc",2,0,1000
- 1,"heap_profile_allocation",-10,2,"malloc",3,0,90
+ 0,"heap_profile_allocation",-10,2,"unknown",2,0,1000
+ 1,"heap_profile_allocation",-10,2,"unknown",3,0,90
"""))
def test_heap_profile_dump_max(self):
@@ -71,6 +71,6 @@
""",
out=Csv("""
"id","type","ts","upid","heap_name","callsite_id","count","size"
- 0,"heap_profile_allocation",-10,2,"malloc",2,6,1000
- 1,"heap_profile_allocation",-10,2,"malloc",3,1,90
+ 0,"heap_profile_allocation",-10,2,"unknown",2,6,1000
+ 1,"heap_profile_allocation",-10,2,"unknown",3,1,90
"""))
diff --git a/tools/gen_tp_table_headers.py b/tools/gen_tp_table_headers.py
index 6d7e044..c53602e 100755
--- a/tools/gen_tp_table_headers.py
+++ b/tools/gen_tp_table_headers.py
@@ -70,7 +70,10 @@
return module_path[module_path.rfind(os.sep + 'src') + 1:]
modules = [
- os.path.splitext(get_relin_path(i).replace('/', '.'))[0]
+ # On Windows the path can contain '/' or os.sep, depending on how this
+ # script is executed. So we need to replace both.
+ os.path.splitext(
+ get_relin_path(i).replace('/', '.').replace(os.sep, '.'))[0]
for i in args.inputs
]
headers: Dict[str, Header] = {}
diff --git a/ui/BUILD.bazel b/ui/BUILD.bazel
deleted file mode 100644
index c3c05f2..0000000
--- a/ui/BUILD.bazel
+++ /dev/null
@@ -1,6 +0,0 @@
-load("@perfetto_cfg//:perfetto_cfg.bzl", "PERFETTO_CONFIG")
-
-exports_files(
- ["src/assets/favicon.png"],
- visibility = PERFETTO_CONFIG.public_visibility,
-)
\ No newline at end of file
diff --git a/ui/build.js b/ui/build.js
index 698842c..56745df 100644
--- a/ui/build.js
+++ b/ui/build.js
@@ -110,11 +110,13 @@
outDistDir: '',
outExtDir: '',
outBigtraceDistDir: '',
+ outOpenPerfettoTraceDistDir: '',
};
const RULES = [
{r: /ui\/src\/assets\/index.html/, f: copyIndexHtml},
{r: /ui\/src\/assets\/bigtrace.html/, f: copyBigtraceHtml},
+ {r: /ui\/src\/open_perfetto_trace\/index.html/, f: copyOpenPerfettoTraceHtml},
{r: /ui\/src\/assets\/((.*)[.]png)/, f: copyAssets},
{r: /buildtools\/typefaces\/(.+[.]woff2)/, f: copyAssets},
{r: /buildtools\/catapult_trace_viewer\/(.+(js|html))/, f: copyAssets},
@@ -149,6 +151,7 @@
parser.add_argument('--run-integrationtests', '-T', {action: 'store_true'});
parser.add_argument('--debug', '-d', {action: 'store_true'});
parser.add_argument('--bigtrace', {action: 'store_true'});
+ parser.add_argument('--open-perfetto-trace', {action: 'store_true'});
parser.add_argument('--interactive', '-i', {action: 'store_true'});
parser.add_argument('--rebaseline', '-r', {action: 'store_true'});
parser.add_argument('--no-depscheck', {action: 'store_true'});
@@ -175,11 +178,16 @@
cfg.verbose = !!args.verbose;
cfg.debug = !!args.debug;
cfg.bigtrace = !!args.bigtrace;
+ cfg.openPerfettoTrace = !!args.open_perfetto_trace;
cfg.startHttpServer = args.serve;
cfg.noOverrideGnArgs = !!args.no_override_gn_args;
if (args.bigtrace) {
cfg.outBigtraceDistDir = ensureDir(pjoin(cfg.outDistDir, 'bigtrace'));
}
+ if (cfg.openPerfettoTrace) {
+ cfg.outOpenPerfettoTraceDistDir = ensureDir(pjoin(cfg.outDistRootDir,
+ 'open_perfetto_trace'));
+ }
if (args.serve_host) {
cfg.httpServerListenHost = args.serve_host;
}
@@ -247,17 +255,25 @@
generateImports('ui/src/plugins', 'all_plugins.ts');
compileProtos();
genVersion();
- transpileTsProject('ui');
- transpileTsProject('ui/src/service_worker');
- if (cfg.bigtrace) {
- transpileTsProject('ui/src/bigtrace');
+
+ const tsProjects = [
+ 'ui',
+ 'ui/src/service_worker'
+ ];
+ if (cfg.bigtrace) tsProjects.push('ui/src/bigtrace');
+ if (cfg.openPerfettoTrace) {
+ scanDir('ui/src/open_perfetto_trace');
+ tsProjects.push('ui/src/open_perfetto_trace');
+ }
+
+
+ for (const prj of tsProjects) {
+ transpileTsProject(prj);
}
if (cfg.watch) {
- transpileTsProject('ui', {watch: cfg.watch});
- transpileTsProject('ui/src/service_worker', {watch: cfg.watch});
- if (cfg.bigtrace) {
- transpileTsProject('ui/src/bigtrace', {watch: cfg.watch});
+ for (const prj of tsProjects) {
+ transpileTsProject(prj, {watch: cfg.watch});
}
}
@@ -350,6 +366,12 @@
}
}
+function copyOpenPerfettoTraceHtml(src) {
+ if (cfg.openPerfettoTrace) {
+ addTask(cp, [src, pjoin(cfg.outOpenPerfettoTraceDistDir, 'index.html')]);
+ }
+}
+
function copyAssets(src, dst) {
addTask(cp, [src, pjoin(cfg.outDistDir, 'assets', dst)]);
if (cfg.bigtrace) {
@@ -519,6 +541,9 @@
if (cfg.bigtrace) {
args.push('--environment', 'ENABLE_BIGTRACE:true');
}
+ if (cfg.openPerfettoTrace) {
+ args.push('--environment', 'ENABLE_OPEN_PERFETTO_TRACE:true');
+ }
args.push(...(cfg.verbose ? [] : ['--silent']));
if (cfg.watch) {
// --waitForBundleInput is sadly quite busted so it is required ts
diff --git a/ui/config/rollup.config.js b/ui/config/rollup.config.js
index f87a99e..144f5d9 100644
--- a/ui/config/rollup.config.js
+++ b/ui/config/rollup.config.js
@@ -97,10 +97,15 @@
[defBundle('tsc/bigtrace', 'bigtrace', 'dist_version/bigtrace')] :
[];
+const maybeOpenPerfettoTrace = process.env['ENABLE_OPEN_PERFETTO_TRACE'] ?
+ [defBundle('tsc', 'open_perfetto_trace', 'dist/open_perfetto_trace')] :
+ [];
+
+
export default [
defBundle('tsc', 'frontend', 'dist_version'),
defBundle('tsc', 'engine', 'dist_version'),
defBundle('tsc', 'traceconv', 'dist_version'),
defBundle('tsc', 'chrome_extension', 'chrome_extension'),
defServiceWorkerBundle(),
-].concat(maybeBigtrace);
+].concat(maybeBigtrace).concat(maybeOpenPerfettoTrace);
diff --git a/ui/release/channels.json b/ui/release/channels.json
index c961c52..c80cab9 100644
--- a/ui/release/channels.json
+++ b/ui/release/channels.json
@@ -6,7 +6,7 @@
},
{
"name": "canary",
- "rev": "9945c86178de704ff44bef7f64c020ff79aedd28"
+ "rev": "4fad83137e7ce34736f5238b7667b1dbdeeb5e2a"
},
{
"name": "autopush",
diff --git a/ui/src/bigtrace/index.ts b/ui/src/bigtrace/index.ts
index d54bd87..739594e 100644
--- a/ui/src/bigtrace/index.ts
+++ b/ui/src/bigtrace/index.ts
@@ -18,7 +18,7 @@
import m from 'mithril';
import {defer} from '../base/deferred';
-import {reportError, setErrorHandler} from '../base/logging';
+import {reportError, addErrorHandler, ErrorDetails} from '../base/logging';
import {initLiveReloadIfLocalhost} from '../core/live_reload';
import {raf} from '../core/raf_scheduler';
import {setScheduleFullRedraw} from '../widgets/raf';
@@ -92,7 +92,7 @@
document.head.append(css);
// Add Error handlers for JS error and for uncaught exceptions in promises.
- setErrorHandler((err: string) => console.log(err));
+ addErrorHandler((err: ErrorDetails) => console.log(err.message, err.stack));
window.addEventListener('error', (e) => reportError(e));
window.addEventListener('unhandledrejection', (e) => reportError(e));
diff --git a/ui/src/common/actions.ts b/ui/src/common/actions.ts
index 92cad97..4cebecb 100644
--- a/ui/src/common/actions.ts
+++ b/ui/src/common/actions.ts
@@ -15,7 +15,7 @@
import {Draft} from 'immer';
import {assertExists, assertTrue} from '../base/logging';
-import {duration, time} from '../base/time';
+import {duration, Time, time} from '../base/time';
import {RecordConfig} from '../controller/record_config_types';
import {
GenericSliceDetailsTabConfig,
@@ -63,7 +63,6 @@
State,
Status,
ThreadTrackSortKey,
- TraceTime,
TrackSortKey,
UtidToTrackSortKey,
VisibleState,
@@ -474,10 +473,6 @@
state.permalink = {};
},
- setTraceTime(state: StateDraft, args: TraceTime): void {
- state.traceTime = args;
- },
-
updateStatus(state: StateDraft, args: Status): void {
if (statusTraceEvent) {
traceEventEnd(statusTraceEvent);
@@ -673,7 +668,7 @@
};
this.openFlamegraph(state, {
type: args.type,
- start: state.traceTime.start as time, // TODO(stevegolton): Avoid type assertion here.
+ start: Time.ZERO,
end: args.ts,
upids: [args.upid],
viewingOption: defaultViewingOption(args.type),
diff --git a/ui/src/common/empty_state.ts b/ui/src/common/empty_state.ts
index f866914..e3ed2b7 100644
--- a/ui/src/common/empty_state.ts
+++ b/ui/src/common/empty_state.ts
@@ -22,12 +22,7 @@
} from '../frontend/record_config';
import {SqlTables} from '../frontend/sql_table/well_known_tables';
-import {
- defaultTraceTime,
- NonSerializableState,
- State,
- STATE_VERSION,
-} from './state';
+import {NonSerializableState, State, STATE_VERSION} from './state';
const AUTOLOAD_STARTED_CONFIG_FLAG = featureFlags.register({
id: 'autoloadStartedConfig',
@@ -92,7 +87,6 @@
version: STATE_VERSION,
nextId: '-1',
newEngineMode: 'USE_HTTP_RPC_IF_AVAILABLE',
- traceTime: {...defaultTraceTime},
tracks: {},
utidToThreadSortKey: {},
aggregatePreferences: {},
@@ -112,7 +106,8 @@
frontendLocalState: {
visibleState: {
- ...defaultTraceTime,
+ start: Time.ZERO,
+ end: Time.ZERO,
lastUpdate: 0,
resolution: 0n,
},
diff --git a/ui/src/common/plugins.ts b/ui/src/common/plugins.ts
index 0f2b2b7..3a5899e 100644
--- a/ui/src/common/plugins.ts
+++ b/ui/src/common/plugins.ts
@@ -21,7 +21,6 @@
import {
Command,
DetailsPanel,
- EngineProxy,
MetricVisualisation,
Migrate,
Plugin,
@@ -36,7 +35,7 @@
GroupPredicate,
TrackRef,
} from '../public';
-import {Engine} from '../trace_processor/engine';
+import {EngineBase, Engine} from '../trace_processor/engine';
import {Actions} from './actions';
import {SCROLLING_TRACK_GROUP} from './state';
@@ -103,9 +102,12 @@
class PluginContextTraceImpl implements PluginContextTrace, Disposable {
private trash = new Trash();
private alive = true;
+ readonly engine: Engine;
- constructor(private ctx: PluginContext, readonly engine: EngineProxy) {
- this.trash.add(engine);
+ constructor(private ctx: PluginContext, engine: EngineBase) {
+ const engineProxy = engine.getProxy(ctx.pluginId);
+ this.trash.add(engineProxy);
+ this.engine = engineProxy;
}
registerCommand(cmd: Command): void {
@@ -380,7 +382,7 @@
export class PluginManager {
private registry: PluginRegistry;
private _plugins: Map<string, PluginDetails>;
- private engine?: Engine;
+ private engine?: EngineBase;
private flags = new Map<string, Flag>();
constructor(registry: PluginRegistry) {
@@ -466,7 +468,7 @@
// If a trace is already loaded when plugin is activated, make sure to
// call onTraceLoad().
if (this.engine) {
- await doPluginTraceLoad(pluginDetails, this.engine, id);
+ await doPluginTraceLoad(pluginDetails, this.engine);
}
this._plugins.set(id, pluginDetails);
@@ -528,7 +530,7 @@
}
async onTraceLoad(
- engine: Engine,
+ engine: EngineBase,
beforeEach?: (id: string) => void,
): Promise<void> {
this.engine = engine;
@@ -546,7 +548,7 @@
// time.
for (const {id, plugin} of pluginsShuffled) {
beforeEach?.(id);
- await doPluginTraceLoad(plugin, engine, id);
+ await doPluginTraceLoad(plugin, engine);
}
}
@@ -571,14 +573,11 @@
async function doPluginTraceLoad(
pluginDetails: PluginDetails,
- engine: Engine,
- pluginId: string,
+ engine: EngineBase,
): Promise<void> {
const {plugin, context} = pluginDetails;
- const engineProxy = engine.getProxy(pluginId);
-
- const traceCtx = new PluginContextTraceImpl(context, engineProxy);
+ const traceCtx = new PluginContextTraceImpl(context, engine);
pluginDetails.traceContext = traceCtx;
const startTime = performance.now();
diff --git a/ui/src/common/plugins_unittest.ts b/ui/src/common/plugins_unittest.ts
index 55fc82d..7efccda 100644
--- a/ui/src/common/plugins_unittest.ts
+++ b/ui/src/common/plugins_unittest.ts
@@ -14,12 +14,12 @@
import {globals} from '../frontend/globals';
import {Plugin} from '../public';
-import {Engine} from '../trace_processor/engine';
+import {EngineBase} from '../trace_processor/engine';
import {createEmptyState} from './empty_state';
import {PluginManager, PluginRegistry} from './plugins';
-class FakeEngine extends Engine {
+class FakeEngine extends EngineBase {
id: string = 'TestEngine';
rpcSendRequestBytes(_data: Uint8Array) {}
diff --git a/ui/src/common/queries.ts b/ui/src/common/queries.ts
index 857d8e9..a6c461b 100644
--- a/ui/src/common/queries.ts
+++ b/ui/src/common/queries.ts
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-import {EngineProxy} from '../trace_processor/engine';
+import {Engine} from '../trace_processor/engine';
import {Row} from '../trace_processor/query_result';
const MAX_DISPLAY_ROWS = 10000;
@@ -36,7 +36,7 @@
export async function runQuery(
sqlQuery: string,
- engine: EngineProxy,
+ engine: Engine,
params?: QueryRunParams,
): Promise<QueryResponse> {
const startMs = performance.now();
diff --git a/ui/src/common/schema.ts b/ui/src/common/schema.ts
index 75a4b2f..c3f33e4 100644
--- a/ui/src/common/schema.ts
+++ b/ui/src/common/schema.ts
@@ -12,10 +12,10 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-import {EngineProxy} from '../trace_processor/engine';
+import {Engine} from '../trace_processor/engine';
import {STR} from '../trace_processor/query_result';
-const CACHED_SCHEMAS = new WeakMap<EngineProxy, DatabaseSchema>();
+const CACHED_SCHEMAS = new WeakMap<Engine, DatabaseSchema>();
export class SchemaError extends Error {
constructor(message: string) {
@@ -40,7 +40,7 @@
}
async function getColumns(
- engine: EngineProxy,
+ engine: Engine,
table: string,
): Promise<ColumnInfo[]> {
const result = await engine.query(`PRAGMA table_info(${table});`);
@@ -83,7 +83,7 @@
// Deliberately not exported. Users should call getSchema below and
// participate in cacheing.
-async function createSchema(engine: EngineProxy): Promise<DatabaseSchema> {
+async function createSchema(engine: Engine): Promise<DatabaseSchema> {
const tables: TableInfo[] = [];
const result = await engine.query(`SELECT name from perfetto_tables;`);
const it = result.iter({
@@ -108,7 +108,7 @@
// The schemas are per-engine (i.e. they can't be statically determined
// at build time) since we might be in httpd mode and not-running
// against the version of trace_processor we build with.
-export async function getSchema(engine: EngineProxy): Promise<DatabaseSchema> {
+export async function getSchema(engine: Engine): Promise<DatabaseSchema> {
const schema = CACHED_SCHEMAS.get(engine);
if (schema === undefined) {
const newSchema = await createSchema(engine);
diff --git a/ui/src/common/state.ts b/ui/src/common/state.ts
index d0af73b..7ca7a74 100644
--- a/ui/src/common/state.ts
+++ b/ui/src/common/state.ts
@@ -13,7 +13,7 @@
// limitations under the License.
import {BigintMath} from '../base/bigint_math';
-import {duration, Time, time} from '../base/time';
+import {duration, time} from '../base/time';
import {RecordConfig} from '../controller/record_config_types';
import {
Aggregation,
@@ -150,7 +150,8 @@
// 51. Changed structure of FlamegraphState.expandedCallsiteByViewingOption.
// 52. Update track group state - don't make the summary track the first track.
// 53. Remove android log state.
-export const STATE_VERSION = 53;
+// 54. Remove traceTime.
+export const STATE_VERSION = 54;
export const SCROLLING_TRACK_GROUP = 'ScrollingTracks';
@@ -315,11 +316,6 @@
isRecordingConfig?: boolean; // this permalink request is for a recording config only
}
-export interface TraceTime {
- start: time;
- end: time;
-}
-
export interface FrontendLocalState {
visibleState: VisibleState;
}
@@ -479,7 +475,6 @@
*/
newEngineMode: NewEngineMode;
engine?: EngineConfig;
- traceTime: TraceTime;
traceUuid?: string;
trackGroups: ObjectById<TrackGroupState>;
tracks: ObjectByKey<TrackState>;
@@ -558,11 +553,6 @@
plugins: {[key: string]: any};
}
-export const defaultTraceTime = {
- start: Time.ZERO,
- end: Time.fromSeconds(10),
-};
-
export declare type RecordMode =
| 'STOP_WHEN_FULL'
| 'RING_BUFFER'
diff --git a/ui/src/common/track_helper.ts b/ui/src/common/track_helper.ts
index 84808df..396b955 100644
--- a/ui/src/common/track_helper.ts
+++ b/ui/src/common/track_helper.ts
@@ -19,16 +19,6 @@
import {raf} from '../core/raf_scheduler';
import {globals} from '../frontend/globals';
-export {EngineProxy} from '../trace_processor/engine';
-export {
- LONG,
- LONG_NULL,
- NUM,
- NUM_NULL,
- STR,
- STR_NULL,
-} from '../trace_processor/query_result';
-
type FetchTimeline<Data> = (
start: time,
end: time,
diff --git a/ui/src/controller/selection_controller.ts b/ui/src/controller/selection_controller.ts
index dc905ea..1b6dd0f 100644
--- a/ui/src/controller/selection_controller.ts
+++ b/ui/src/controller/selection_controller.ts
@@ -441,7 +441,7 @@
IFNULL(value, 0) as value
FROM counter WHERE ts < ${ts} and track_id = ${trackId}`);
const previousValue = previous.firstRow({value: NUM}).value;
- const endTs = rightTs !== -1n ? rightTs : globals.state.traceTime.end;
+ const endTs = rightTs !== -1n ? rightTs : globals.traceTime.end;
const delta = value - previousValue;
const duration = endTs - ts;
const trackKey = globals.trackManager.trackKeyByTrackId.get(trackId);
diff --git a/ui/src/controller/trace_controller.ts b/ui/src/controller/trace_controller.ts
index c1f0d6e..445bd4a 100644
--- a/ui/src/controller/trace_controller.ts
+++ b/ui/src/controller/trace_controller.ts
@@ -26,25 +26,26 @@
isMetatracingEnabled,
} from '../common/metatracing';
import {pluginManager} from '../common/plugins';
+import {EngineMode, PendingDeeplinkState, ProfileType} from '../common/state';
+import {featureFlags, Flag, PERF_SAMPLE_FLAG} from '../core/feature_flags';
import {
defaultTraceTime,
- EngineMode,
- PendingDeeplinkState,
- ProfileType,
-} from '../common/state';
-import {featureFlags, Flag, PERF_SAMPLE_FLAG} from '../core/feature_flags';
-import {globals, QuantizedLoad, ThreadDesc} from '../frontend/globals';
+ globals,
+ QuantizedLoad,
+ ThreadDesc,
+ TraceTime,
+} from '../frontend/globals';
import {
clearOverviewData,
publishHasFtrace,
publishMetricError,
publishOverviewData,
- publishRealtimeOffset,
publishThreads,
+ publishTraceDetails,
} from '../frontend/publish';
import {addQueryResultsTab} from '../frontend/query_result_tab';
import {Router} from '../frontend/router';
-import {Engine} from '../trace_processor/engine';
+import {Engine, EngineBase} from '../trace_processor/engine';
import {HttpRpcEngine} from '../trace_processor/http_rpc_engine';
import {
LONG,
@@ -224,7 +225,7 @@
// trace opened in the UI (for now only one trace is supported).
export class TraceController extends Controller<States> {
private readonly engineId: string;
- private engine?: Engine;
+ private engine?: EngineBase;
constructor(engineId: string) {
super('init');
@@ -450,13 +451,8 @@
// traceUuid will be '' if the trace is not cacheable (URL or RPC).
const traceUuid = await this.cacheCurrentTrace();
- const traceTime = await this.engine.getTraceTimeBounds();
- const start = traceTime.start;
- const end = traceTime.end;
- const traceTimeState = {
- start,
- end,
- };
+ const traceDetails = await getTraceTimeDetails(this.engine);
+ publishTraceDetails(traceDetails);
const shownJsonWarning =
window.localStorage.getItem(SHOWN_JSON_WARNING_KEY) !== null;
@@ -485,12 +481,11 @@
const actions: DeferredAction[] = [
Actions.setOmnibox(emptyOmniboxState),
Actions.setTraceUuid({traceUuid}),
- Actions.setTraceTime(traceTimeState),
];
const visibleTimeSpan = await computeVisibleTime(
- traceTime.start,
- traceTime.end,
+ traceDetails.start,
+ traceDetails.end,
isJsonTrace,
this.engine,
);
@@ -530,7 +525,9 @@
this.decideTabs();
await this.listThreads();
- await this.loadTimelineOverview(traceTime);
+ await this.loadTimelineOverview(
+ new TimeSpan(traceDetails.start, traceDetails.end),
+ );
{
// Check if we have any ftrace events at all
@@ -544,82 +541,12 @@
publishHasFtrace(res.numRows() > 0);
}
- {
- // Find the first REALTIME or REALTIME_COARSE clock snapshot.
- // Prioritize REALTIME over REALTIME_COARSE.
- const query = `select
- ts,
- clock_value as clockValue,
- clock_name as clockName
- from clock_snapshot
- where
- snapshot_id = 0 AND
- clock_name in ('REALTIME', 'REALTIME_COARSE')
- `;
- const result = await assertExists(this.engine).query(query);
- const it = result.iter({
- ts: LONG,
- clockValue: LONG,
- clockName: STR,
- });
-
- let snapshot = {
- clockName: '',
- ts: Time.ZERO,
- clockValue: Time.ZERO,
- };
-
- // Find the most suitable snapshot
- for (let row = 0; it.valid(); it.next(), row++) {
- if (it.clockName === 'REALTIME') {
- snapshot = {
- clockName: it.clockName,
- ts: Time.fromRaw(it.ts),
- clockValue: Time.fromRaw(it.clockValue),
- };
- break;
- } else if (it.clockName === 'REALTIME_COARSE') {
- if (snapshot.clockName !== 'REALTIME') {
- snapshot = {
- clockName: it.clockName,
- ts: Time.fromRaw(it.ts),
- clockValue: Time.fromRaw(it.clockValue),
- };
- }
- }
- }
-
- // The max() is so the query returns NULL if the tz info doesn't exist.
- const queryTz = `select max(int_value) as tzOffMin from metadata
- where name = 'timezone_off_mins'`;
- const resTz = await assertExists(this.engine).query(queryTz);
- const tzOffMin = resTz.firstRow({tzOffMin: NUM_NULL}).tzOffMin ?? 0;
-
- // This is the offset between the unix epoch and ts in the ts domain.
- // I.e. the value of ts at the time of the unix epoch - usually some large
- // negative value.
- const realtimeOffset = Time.sub(snapshot.ts, snapshot.clockValue);
-
- // Find the previous closest midnight from the trace start time.
- const utcOffset = Time.getLatestMidnight(
- globals.state.traceTime.start,
- realtimeOffset,
- );
-
- const traceTzOffset = Time.getLatestMidnight(
- globals.state.traceTime.start,
- Time.sub(realtimeOffset, Time.fromSeconds(tzOffMin * 60)),
- );
-
- publishRealtimeOffset(realtimeOffset, utcOffset, traceTzOffset);
- }
-
globals.dispatch(Actions.sortThreadTracks({}));
globals.dispatch(Actions.maybeExpandOnlyTrackGroup({}));
await this.selectFirstHeapProfile();
if (PERF_SAMPLE_FLAG.get()) {
- await this.selectPerfSample();
+ await this.selectPerfSample(traceDetails);
}
const pendingDeeplink = globals.state.pendingDeeplink;
@@ -663,7 +590,7 @@
return engineMode;
}
- private async selectPerfSample() {
+ private async selectPerfSample(traceTime: {start: time; end: time}) {
const query = `select upid
from perf_sample
join thread using (utid)
@@ -673,8 +600,8 @@
if (profile.numRows() !== 1) return;
const row = profile.firstRow({upid: NUM});
const upid = row.upid;
- const leftTs = globals.state.traceTime.start;
- const rightTs = globals.state.traceTime.end;
+ const leftTs = traceTime.start;
+ const rightTs = traceTime.end;
globals.dispatch(
Actions.selectPerfSamples({
id: 0,
@@ -766,7 +693,7 @@
private async listTracks() {
this.updateStatus('Loading tracks');
- const engine = assertExists<Engine>(this.engine);
+ const engine = assertExists(this.engine);
const actions = await decideTracks(engine);
globals.dispatchMultiple(actions);
}
@@ -918,7 +845,7 @@
}
async initialiseHelperViews() {
- const engine = assertExists<Engine>(this.engine);
+ const engine = assertExists(this.engine);
this.updateStatus('Creating annotation counter track table');
// Create the helper tables for all the annotations related data.
@@ -1217,3 +1144,77 @@
}
return HighPrecisionTimeSpan.fromTime(visibleStart, visibleEnd);
}
+
+async function getTraceTimeDetails(engine: EngineBase): Promise<TraceTime> {
+ const traceTime = await engine.getTraceTimeBounds();
+
+ // Find the first REALTIME or REALTIME_COARSE clock snapshot.
+ // Prioritize REALTIME over REALTIME_COARSE.
+ const query = `select
+ ts,
+ clock_value as clockValue,
+ clock_name as clockName
+ from clock_snapshot
+ where
+ snapshot_id = 0 AND
+ clock_name in ('REALTIME', 'REALTIME_COARSE')
+ `;
+ const result = await engine.query(query);
+ const it = result.iter({
+ ts: LONG,
+ clockValue: LONG,
+ clockName: STR,
+ });
+
+ let snapshot = {
+ clockName: '',
+ ts: Time.ZERO,
+ clockValue: Time.ZERO,
+ };
+
+ // Find the most suitable snapshot
+ for (let row = 0; it.valid(); it.next(), row++) {
+ if (it.clockName === 'REALTIME') {
+ snapshot = {
+ clockName: it.clockName,
+ ts: Time.fromRaw(it.ts),
+ clockValue: Time.fromRaw(it.clockValue),
+ };
+ break;
+ } else if (it.clockName === 'REALTIME_COARSE') {
+ if (snapshot.clockName !== 'REALTIME') {
+ snapshot = {
+ clockName: it.clockName,
+ ts: Time.fromRaw(it.ts),
+ clockValue: Time.fromRaw(it.clockValue),
+ };
+ }
+ }
+ }
+
+ // The max() is so the query returns NULL if the tz info doesn't exist.
+ const queryTz = `select max(int_value) as tzOffMin from metadata
+ where name = 'timezone_off_mins'`;
+ const resTz = await assertExists(engine).query(queryTz);
+ const tzOffMin = resTz.firstRow({tzOffMin: NUM_NULL}).tzOffMin ?? 0;
+
+ // This is the offset between the unix epoch and ts in the ts domain.
+ // I.e. the value of ts at the time of the unix epoch - usually some large
+ // negative value.
+ const realtimeOffset = Time.sub(snapshot.ts, snapshot.clockValue);
+
+ // Find the previous closest midnight from the trace start time.
+ const utcOffset = Time.getLatestMidnight(traceTime.start, realtimeOffset);
+
+ const traceTzOffset = Time.getLatestMidnight(
+ traceTime.start,
+ Time.sub(realtimeOffset, Time.fromSeconds(tzOffMin * 60)),
+ );
+
+ return {
+ ...traceTime,
+ realtimeOffset,
+ utcOffset,
+ traceTzOffset,
+ };
+}
diff --git a/ui/src/controller/track_decider.ts b/ui/src/controller/track_decider.ts
index f0c7ef4..e1b907c 100644
--- a/ui/src/controller/track_decider.ts
+++ b/ui/src/controller/track_decider.ts
@@ -27,7 +27,7 @@
import {PERF_SAMPLE_FLAG} from '../core/feature_flags';
import {PrimaryTrackSortKey} from '../public';
import {getTrackName} from '../public/utils';
-import {Engine, EngineProxy} from '../trace_processor/engine';
+import {Engine, EngineBase} from '../trace_processor/engine';
import {NUM, NUM_NULL, STR, STR_NULL} from '../trace_processor/query_result';
import {ASYNC_SLICE_TRACK_KIND} from '../core_plugins/async_slices';
import {
@@ -76,18 +76,20 @@
const CHROME_TRACK_GROUP = 'Chrome Global Tracks';
const MISC_GROUP = 'Misc Global Tracks';
-export async function decideTracks(engine: Engine): Promise<DeferredAction[]> {
+export async function decideTracks(
+ engine: EngineBase,
+): Promise<DeferredAction[]> {
return new TrackDecider(engine).decideTracks();
}
class TrackDecider {
- private engine: Engine;
+ private engine: EngineBase;
private upidToUuid = new Map<number, string>();
private utidToUuid = new Map<number, string>();
private tracksToAdd: AddTrackArgs[] = [];
private addTrackGroupActions: DeferredAction[] = [];
- constructor(engine: Engine) {
+ constructor(engine: EngineBase) {
this.engine = engine;
}
@@ -131,7 +133,7 @@
}
}
- async addCpuFreqTracks(engine: EngineProxy): Promise<void> {
+ async addCpuFreqTracks(engine: Engine): Promise<void> {
const cpus = await this.engine.getCpus();
for (const cpu of cpus) {
@@ -165,7 +167,7 @@
}
}
- async addGlobalAsyncTracks(engine: EngineProxy): Promise<void> {
+ async addGlobalAsyncTracks(engine: Engine): Promise<void> {
const rawGlobalAsyncTracks = await engine.query(`
with global_tracks_grouped as (
select distinct t.parent_id, t.name
@@ -226,7 +228,7 @@
}
}
- async addGpuFreqTracks(engine: EngineProxy): Promise<void> {
+ async addGpuFreqTracks(engine: Engine): Promise<void> {
const numGpus = await this.engine.getNumberOfGpus();
for (let gpu = 0; gpu < numGpus; gpu++) {
// Only add a gpu freq track if we have
@@ -248,7 +250,7 @@
}
}
- async addCpuFreqLimitCounterTracks(engine: EngineProxy): Promise<void> {
+ async addCpuFreqLimitCounterTracks(engine: Engine): Promise<void> {
const cpuFreqLimitCounterTracksSql = `
select name, id
from cpu_counter_track
@@ -259,7 +261,7 @@
this.addCpuCounterTracks(engine, cpuFreqLimitCounterTracksSql);
}
- async addCpuPerfCounterTracks(engine: EngineProxy): Promise<void> {
+ async addCpuPerfCounterTracks(engine: Engine): Promise<void> {
// Perf counter tracks are bound to CPUs, follow the scheduling and
// frequency track naming convention ("Cpu N ...").
// Note: we might not have a track for a given cpu if no data was seen from
@@ -274,7 +276,7 @@
this.addCpuCounterTracks(engine, addCpuPerfCounterTracksSql);
}
- async addCpuCounterTracks(engine: EngineProxy, sql: string): Promise<void> {
+ async addCpuCounterTracks(engine: Engine, sql: string): Promise<void> {
const result = await engine.query(sql);
const it = result.iter({
@@ -516,7 +518,7 @@
}
}
- async addAnnotationTracks(engine: EngineProxy): Promise<void> {
+ async addAnnotationTracks(engine: Engine): Promise<void> {
const sliceResult = await engine.query(`
select id, name, upid, group_name
from annotation_slice_track
@@ -607,7 +609,7 @@
}
}
- async addThreadStateTracks(engine: EngineProxy): Promise<void> {
+ async addThreadStateTracks(engine: Engine): Promise<void> {
const result = await engine.query(`
select
utid,
@@ -657,7 +659,7 @@
}
}
- async addThreadCpuSampleTracks(engine: EngineProxy): Promise<void> {
+ async addThreadCpuSampleTracks(engine: Engine): Promise<void> {
const result = await engine.query(`
with thread_cpu_sample as (
select distinct utid
@@ -695,7 +697,7 @@
}
}
- async addThreadCounterTracks(engine: EngineProxy): Promise<void> {
+ async addThreadCounterTracks(engine: Engine): Promise<void> {
const result = await engine.query(`
select
thread_counter_track.name as trackName,
@@ -745,7 +747,7 @@
}
}
- async addProcessAsyncSliceTracks(engine: EngineProxy): Promise<void> {
+ async addProcessAsyncSliceTracks(engine: Engine): Promise<void> {
const result = await engine.query(`
select
upid,
@@ -790,7 +792,7 @@
}
}
- async addUserAsyncSliceTracks(engine: EngineProxy): Promise<void> {
+ async addUserAsyncSliceTracks(engine: Engine): Promise<void> {
const result = await engine.query(`
with grouped_packages as materialized (
select
@@ -848,7 +850,7 @@
}
}
- async addActualFramesTracks(engine: EngineProxy): Promise<void> {
+ async addActualFramesTracks(engine: Engine): Promise<void> {
const result = await engine.query(`
select
upid,
@@ -891,7 +893,7 @@
}
}
- async addExpectedFramesTracks(engine: EngineProxy): Promise<void> {
+ async addExpectedFramesTracks(engine: Engine): Promise<void> {
const result = await engine.query(`
select
upid,
@@ -935,7 +937,7 @@
}
}
- async addThreadSliceTracks(engine: EngineProxy): Promise<void> {
+ async addThreadSliceTracks(engine: Engine): Promise<void> {
const result = await engine.query(`
select
thread_track.utid as utid,
@@ -990,7 +992,7 @@
}
}
- async addProcessCounterTracks(engine: EngineProxy): Promise<void> {
+ async addProcessCounterTracks(engine: Engine): Promise<void> {
const result = await engine.query(`
select
process_counter_track.id as trackId,
@@ -1034,7 +1036,7 @@
}
}
- async addProcessHeapProfileTracks(engine: EngineProxy): Promise<void> {
+ async addProcessHeapProfileTracks(engine: Engine): Promise<void> {
const result = await engine.query(`
select upid
from _process_available_info_summary
@@ -1052,7 +1054,7 @@
}
}
- async addProcessPerfSamplesTracks(engine: EngineProxy): Promise<void> {
+ async addProcessPerfSamplesTracks(engine: Engine): Promise<void> {
const result = await engine.query(`
select upid, pid
from _process_available_info_summary
@@ -1099,7 +1101,7 @@
this.upidToUuid.set(upid, uuid);
}
- async addKernelThreadGrouping(engine: EngineProxy): Promise<void> {
+ async addKernelThreadGrouping(engine: Engine): Promise<void> {
// Identify kernel threads if this is a linux system trace, and sufficient
// process information is available. Kernel threads are identified by being
// children of kthreadd (always pid 2).
@@ -1162,7 +1164,7 @@
}
}
- async addProcessTrackGroups(engine: EngineProxy): Promise<void> {
+ async addProcessTrackGroups(engine: Engine): Promise<void> {
// We want to create groups of tracks in a specific order.
// The tracks should be grouped:
// by upid
diff --git a/ui/src/core_plugins/android_log/logs_panel.ts b/ui/src/core_plugins/android_log/logs_panel.ts
index e326a38..ecb1a3b 100644
--- a/ui/src/core_plugins/android_log/logs_panel.ts
+++ b/ui/src/core_plugins/android_log/logs_panel.ts
@@ -21,7 +21,7 @@
import {globals} from '../../frontend/globals';
import {Timestamp} from '../../frontend/widgets/timestamp';
-import {EngineProxy, LONG, NUM, NUM_NULL, Store, STR} from '../../public';
+import {Engine, LONG, NUM, NUM_NULL, Store, STR} from '../../public';
import {Monitor} from '../../base/monitor';
import {AsyncLimiter} from '../../base/async_limiter';
import {escapeGlob, escapeQuery} from '../../trace_processor/query_utils';
@@ -43,7 +43,7 @@
export interface LogPanelAttrs {
filterStore: Store<LogFilteringCriteria>;
- engine: EngineProxy;
+ engine: Engine;
}
interface Pagination {
@@ -384,7 +384,7 @@
}
async function updateLogEntries(
- engine: EngineProxy,
+ engine: Engine,
span: Span<time, duration>,
pagination: Pagination,
): Promise<LogEntries> {
@@ -450,10 +450,7 @@
};
}
-async function updateLogView(
- engine: EngineProxy,
- filter: LogFilteringCriteria,
-) {
+async function updateLogView(engine: Engine, filter: LogFilteringCriteria) {
await engine.query('drop view if exists filtered_logs');
const globMatch = composeGlobMatch(filter.hideNonMatching, filter.textEntry);
diff --git a/ui/src/core_plugins/android_log/logs_track.ts b/ui/src/core_plugins/android_log/logs_track.ts
index 1c5b3c6..7027a65 100644
--- a/ui/src/core_plugins/android_log/logs_track.ts
+++ b/ui/src/core_plugins/android_log/logs_track.ts
@@ -14,11 +14,11 @@
import {Time, duration, time} from '../../base/time';
import {LIMIT, TrackData} from '../../common/track_data';
-import {LONG, NUM, TimelineFetcher} from '../../common/track_helper';
+import {TimelineFetcher} from '../../common/track_helper';
import {checkerboardExcept} from '../../frontend/checkerboard';
import {globals} from '../../frontend/globals';
import {PanelSize} from '../../frontend/panel';
-import {EngineProxy, Track} from '../../public';
+import {Engine, LONG, NUM, Track} from '../../public';
export interface Data extends TrackData {
// Total number of log events within [start, end], before any quantization.
@@ -52,7 +52,7 @@
export class AndroidLogTrack implements Track {
private fetcher = new TimelineFetcher<Data>(this.onBoundsChange.bind(this));
- constructor(private engine: EngineProxy) {}
+ constructor(private engine: Engine) {}
async onUpdate(): Promise<void> {
await this.fetcher.requestDataForCurrentTime();
diff --git a/ui/src/core_plugins/chrome_scroll_jank/index.ts b/ui/src/core_plugins/chrome_scroll_jank/index.ts
index 1feb5cc..4267f4c 100644
--- a/ui/src/core_plugins/chrome_scroll_jank/index.ts
+++ b/ui/src/core_plugins/chrome_scroll_jank/index.ts
@@ -25,7 +25,7 @@
PluginContextTrace,
PluginDescriptor,
} from '../../public';
-import {Engine, EngineProxy} from '../../trace_processor/engine';
+import {Engine} from '../../trace_processor/engine';
import {ChromeTasksScrollJankTrack} from './chrome_tasks_scroll_jank_track';
import {EventLatencySliceDetailsPanel} from './event_latency_details_panel';
@@ -329,7 +329,7 @@
}
}
-async function isChromeTrace(engine: EngineProxy) {
+async function isChromeTrace(engine: Engine) {
const queryResult = await engine.query(`
select utid, upid
from thread
diff --git a/ui/src/core_plugins/chrome_scroll_jank/scroll_delta_graph.ts b/ui/src/core_plugins/chrome_scroll_jank/scroll_delta_graph.ts
index d476019..a2f644b 100644
--- a/ui/src/core_plugins/chrome_scroll_jank/scroll_delta_graph.ts
+++ b/ui/src/core_plugins/chrome_scroll_jank/scroll_delta_graph.ts
@@ -15,7 +15,7 @@
import m from 'mithril';
import {duration, Time, time} from '../../base/time';
-import {EngineProxy} from '../../trace_processor/engine';
+import {Engine} from '../../trace_processor/engine';
import {LONG, NUM} from '../../trace_processor/query_result';
import {VegaView} from '../../widgets/vega_view';
@@ -45,7 +45,7 @@
}
export async function getUserScrollDeltas(
- engine: EngineProxy,
+ engine: Engine,
startTs: time,
dur: duration,
): Promise<ScrollDeltaDetails[]> {
@@ -82,7 +82,7 @@
}
export async function getAppliedScrollDeltas(
- engine: EngineProxy,
+ engine: Engine,
startTs: time,
dur: duration,
): Promise<ScrollDeltaDetails[]> {
@@ -123,7 +123,7 @@
}
export async function getJankIntervals(
- engine: EngineProxy,
+ engine: Engine,
startTs: time,
dur: duration,
): Promise<JankIntervalPlotDetails[]> {
diff --git a/ui/src/core_plugins/chrome_scroll_jank/scroll_jank_cause_link_utils.ts b/ui/src/core_plugins/chrome_scroll_jank/scroll_jank_cause_link_utils.ts
index 64e8784..80ee9e2 100644
--- a/ui/src/core_plugins/chrome_scroll_jank/scroll_jank_cause_link_utils.ts
+++ b/ui/src/core_plugins/chrome_scroll_jank/scroll_jank_cause_link_utils.ts
@@ -24,7 +24,7 @@
verticalScrollToTrack,
} from '../../frontend/scroll_helper';
import {SliceSqlId} from '../../frontend/sql_types';
-import {EngineProxy} from '../../trace_processor/engine';
+import {Engine} from '../../trace_processor/engine';
import {LONG, NUM, STR} from '../../trace_processor/query_result';
import {Anchor} from '../../widgets/anchor';
@@ -53,7 +53,7 @@
}
export async function getScrollJankCauseStage(
- engine: EngineProxy,
+ engine: Engine,
eventLatencyId: SliceSqlId,
): Promise<EventLatencyStage | undefined> {
const queryResult = await engine.query(`
@@ -95,7 +95,7 @@
}
export async function getEventLatencyCauseTracks(
- engine: EngineProxy,
+ engine: Engine,
scrollJankCauseStage: EventLatencyStage,
): Promise<EventLatencyCauseThreadTracks[]> {
const threadTracks: EventLatencyCauseThreadTracks[] = [];
@@ -130,7 +130,7 @@
}
async function getChromeCauseTracks(
- engine: EngineProxy,
+ engine: Engine,
eventLatencySliceId: number,
processName: CauseProcess,
threadName: CauseThread,
diff --git a/ui/src/core_plugins/chrome_scroll_jank/scroll_jank_slice.ts b/ui/src/core_plugins/chrome_scroll_jank/scroll_jank_slice.ts
index c05ac43..6dab538 100644
--- a/ui/src/core_plugins/chrome_scroll_jank/scroll_jank_slice.ts
+++ b/ui/src/core_plugins/chrome_scroll_jank/scroll_jank_slice.ts
@@ -24,7 +24,7 @@
constraintsToQuerySuffix,
SQLConstraints,
} from '../../frontend/sql_utils';
-import {EngineProxy} from '../../trace_processor/engine';
+import {Engine} from '../../trace_processor/engine';
import {LONG, NUM} from '../../trace_processor/query_result';
import {Anchor} from '../../widgets/anchor';
@@ -45,7 +45,7 @@
}
async function getSlicesFromTrack(
- engine: EngineProxy,
+ engine: Engine,
track: ScrollJankTrackSpec,
constraints: SQLConstraints,
): Promise<BasicSlice[]> {
@@ -75,7 +75,7 @@
export type ScrollJankSlice = BasicSlice;
export async function getScrollJankSlices(
- engine: EngineProxy,
+ engine: Engine,
id: number,
): Promise<ScrollJankSlice[]> {
const track = ScrollJankPluginState.getInstance().getTrack(
@@ -93,7 +93,7 @@
export type EventLatencySlice = BasicSlice;
export async function getEventLatencySlice(
- engine: EngineProxy,
+ engine: Engine,
id: number,
): Promise<EventLatencySlice | undefined> {
const track = ScrollJankPluginState.getInstance().getTrack(
@@ -112,7 +112,7 @@
}
export async function getEventLatencyDescendantSlice(
- engine: EngineProxy,
+ engine: Engine,
id: number,
descendant: string | undefined,
): Promise<EventLatencySlice | undefined> {
diff --git a/ui/src/core_plugins/chrome_scroll_jank/scroll_jank_v3_details_panel.ts b/ui/src/core_plugins/chrome_scroll_jank/scroll_jank_v3_details_panel.ts
index b117b08..646b7c6 100644
--- a/ui/src/core_plugins/chrome_scroll_jank/scroll_jank_v3_details_panel.ts
+++ b/ui/src/core_plugins/chrome_scroll_jank/scroll_jank_v3_details_panel.ts
@@ -24,7 +24,7 @@
import {sqlValueToString} from '../../frontend/sql_utils';
import {DurationWidget} from '../../frontend/widgets/duration';
import {Timestamp} from '../../frontend/widgets/timestamp';
-import {EngineProxy} from '../../trace_processor/engine';
+import {Engine} from '../../trace_processor/engine';
import {LONG, NUM, STR} from '../../trace_processor/query_result';
import {DetailsShell} from '../../widgets/details_shell';
import {GridLayout, GridLayoutColumn} from '../../widgets/grid_layout';
@@ -61,7 +61,7 @@
}
async function getSliceDetails(
- engine: EngineProxy,
+ engine: Engine,
id: number,
): Promise<SliceDetails | undefined> {
return getSlice(engine, asSliceSqlId(id));
diff --git a/ui/src/core_plugins/cpu_freq/index.ts b/ui/src/core_plugins/cpu_freq/index.ts
index b5fa5fa..07b9b1d 100644
--- a/ui/src/core_plugins/cpu_freq/index.ts
+++ b/ui/src/core_plugins/cpu_freq/index.ts
@@ -24,7 +24,7 @@
import {globals} from '../../frontend/globals';
import {PanelSize} from '../../frontend/panel';
import {
- EngineProxy,
+ Engine,
Plugin,
PluginContextTrace,
PluginDescriptor,
@@ -62,11 +62,11 @@
private hoveredIdle: number | undefined = undefined;
private fetcher = new TimelineFetcher<Data>(this.onBoundsChange.bind(this));
- private engine: EngineProxy;
+ private engine: Engine;
private config: Config;
private trackUuid = uuidv4Sql();
- constructor(config: Config, engine: EngineProxy) {
+ constructor(config: Config, engine: Engine) {
this.config = config;
this.engine = engine;
}
diff --git a/ui/src/core_plugins/cpu_profile/index.ts b/ui/src/core_plugins/cpu_profile/index.ts
index 4d3e87f..364225d 100644
--- a/ui/src/core_plugins/cpu_profile/index.ts
+++ b/ui/src/core_plugins/cpu_profile/index.ts
@@ -24,7 +24,7 @@
import {PanelSize} from '../../frontend/panel';
import {TimeScale} from '../../frontend/time_scale';
import {
- EngineProxy,
+ Engine,
Plugin,
PluginContextTrace,
PluginDescriptor,
@@ -54,10 +54,10 @@
private markerWidth = (this.getHeight() - MARGIN_TOP - BAR_HEIGHT) / 2;
private hoveredTs: time | undefined = undefined;
private fetcher = new TimelineFetcher<Data>(this.onBoundsChange.bind(this));
- private engine: EngineProxy;
+ private engine: Engine;
private utid: number;
- constructor(engine: EngineProxy, utid: number) {
+ constructor(engine: Engine, utid: number) {
this.engine = engine;
this.utid = utid;
}
diff --git a/ui/src/core_plugins/cpu_slices/index.ts b/ui/src/core_plugins/cpu_slices/index.ts
index 139bac5..1fc1e67 100644
--- a/ui/src/core_plugins/cpu_slices/index.ts
+++ b/ui/src/core_plugins/cpu_slices/index.ts
@@ -33,7 +33,7 @@
import {PanelSize} from '../../frontend/panel';
import {SliceDetailsPanel} from '../../frontend/slice_details_panel';
import {
- EngineProxy,
+ Engine,
Plugin,
PluginContextTrace,
PluginDescriptor,
@@ -67,12 +67,12 @@
private fetcher = new TimelineFetcher<Data>(this.onBoundsChange.bind(this));
private lastRowId = -1;
- private engine: EngineProxy;
+ private engine: Engine;
private cpu: number;
private trackKey: string;
private trackUuid = uuidv4Sql();
- constructor(engine: EngineProxy, trackKey: string, cpu: number) {
+ constructor(engine: Engine, trackKey: string, cpu: number) {
this.engine = engine;
this.trackKey = trackKey;
this.cpu = cpu;
@@ -490,7 +490,7 @@
});
}
- async guessCpuSizes(engine: EngineProxy): Promise<Map<number, string>> {
+ async guessCpuSizes(engine: Engine): Promise<Map<number, string>> {
const cpuToSize = new Map<number, string>();
await engine.query(`
INCLUDE PERFETTO MODULE cpu.size;
diff --git a/ui/src/core_plugins/debug/add_debug_track_menu.ts b/ui/src/core_plugins/debug/add_debug_track_menu.ts
index e03c9f2..a0c5b31 100644
--- a/ui/src/core_plugins/debug/add_debug_track_menu.ts
+++ b/ui/src/core_plugins/debug/add_debug_track_menu.ts
@@ -16,7 +16,7 @@
import {findRef} from '../../base/dom_utils';
import {raf} from '../../core/raf_scheduler';
-import {EngineProxy} from '../../trace_processor/engine';
+import {Engine} from '../../trace_processor/engine';
import {Form, FormLabel} from '../../widgets/form';
import {Select} from '../../widgets/select';
import {TextInput} from '../../widgets/text_input';
@@ -36,7 +36,7 @@
interface AddDebugTrackMenuAttrs {
dataSource: Required<SqlDataSource>;
- engine: EngineProxy;
+ engine: Engine;
}
const TRACK_NAME_FIELD_REF = 'TRACK_NAME_FIELD';
diff --git a/ui/src/core_plugins/debug/counter_track.ts b/ui/src/core_plugins/debug/counter_track.ts
index 5b37e3d..56274d3 100644
--- a/ui/src/core_plugins/debug/counter_track.ts
+++ b/ui/src/core_plugins/debug/counter_track.ts
@@ -16,7 +16,7 @@
import {BaseCounterTrack} from '../../frontend/base_counter_track';
import {TrackContext} from '../../public';
-import {EngineProxy} from '../../trace_processor/engine';
+import {Engine} from '../../trace_processor/engine';
import {CounterDebugTrackConfig} from '../../frontend/debug_tracks';
import {Disposable, DisposableCallback} from '../../base/disposable';
import {uuidv4Sql} from '../../base/uuid';
@@ -25,7 +25,7 @@
private config: CounterDebugTrackConfig;
private sqlTableName: string;
- constructor(engine: EngineProxy, ctx: TrackContext) {
+ constructor(engine: Engine, ctx: TrackContext) {
super({
engine,
trackKey: ctx.trackKey,
diff --git a/ui/src/core_plugins/debug/slice_track.ts b/ui/src/core_plugins/debug/slice_track.ts
index a53e4dc..49e5142 100644
--- a/ui/src/core_plugins/debug/slice_track.ts
+++ b/ui/src/core_plugins/debug/slice_track.ts
@@ -14,7 +14,7 @@
import {NamedSliceTrackTypes} from '../../frontend/named_slice_track';
import {TrackContext} from '../../public';
-import {EngineProxy} from '../../trace_processor/engine';
+import {Engine} from '../../trace_processor/engine';
import {
CustomSqlDetailsPanelConfig,
CustomSqlTableDefConfig,
@@ -40,7 +40,7 @@
private config: DebugTrackV2Config;
private sqlTableName: string;
- constructor(engine: EngineProxy, ctx: TrackContext) {
+ constructor(engine: Engine, ctx: TrackContext) {
super({
engine,
trackKey: ctx.trackKey,
diff --git a/ui/src/core_plugins/frames/actual_frames_track_v2.ts b/ui/src/core_plugins/frames/actual_frames_track_v2.ts
index 6ce0e3d..10b7d72 100644
--- a/ui/src/core_plugins/frames/actual_frames_track_v2.ts
+++ b/ui/src/core_plugins/frames/actual_frames_track_v2.ts
@@ -20,7 +20,7 @@
NamedSliceTrackTypes,
} from '../../frontend/named_slice_track';
import {SLICE_LAYOUT_FIT_CONTENT_DEFAULTS} from '../../frontend/slice_layout';
-import {EngineProxy, Slice, STR_NULL} from '../../public';
+import {Engine, Slice, STR_NULL} from '../../public';
// color named and defined based on Material Design color palettes
// 500 colors indicate a timeline slice is not a partial jank (not a jank or
@@ -54,7 +54,7 @@
export class ActualFramesTrack extends NamedSliceTrack<ActualFrameTrackTypes> {
constructor(
- engine: EngineProxy,
+ engine: Engine,
maxDepth: number,
trackKey: string,
private trackIds: number[],
diff --git a/ui/src/core_plugins/frames/expected_frames_track_v2.ts b/ui/src/core_plugins/frames/expected_frames_track_v2.ts
index ce602a1..e9cce12 100644
--- a/ui/src/core_plugins/frames/expected_frames_track_v2.ts
+++ b/ui/src/core_plugins/frames/expected_frames_track_v2.ts
@@ -16,13 +16,13 @@
import {makeColorScheme} from '../../core/colorizer';
import {NamedRow, NamedSliceTrack} from '../../frontend/named_slice_track';
import {SLICE_LAYOUT_FIT_CONTENT_DEFAULTS} from '../../frontend/slice_layout';
-import {EngineProxy, Slice} from '../../public';
+import {Engine, Slice} from '../../public';
const GREEN = makeColorScheme(new HSLColor('#4CAF50')); // Green 500
export class ExpectedFramesTrack extends NamedSliceTrack {
constructor(
- engine: EngineProxy,
+ engine: Engine,
maxDepth: number,
trackKey: string,
private trackIds: number[],
diff --git a/ui/src/core_plugins/ftrace/ftrace_explorer.ts b/ui/src/core_plugins/ftrace/ftrace_explorer.ts
index d8cad78..e034183 100644
--- a/ui/src/core_plugins/ftrace/ftrace_explorer.ts
+++ b/ui/src/core_plugins/ftrace/ftrace_explorer.ts
@@ -28,7 +28,7 @@
import {globals} from '../../frontend/globals';
import {Timestamp} from '../../frontend/widgets/timestamp';
import {FtraceFilter, FtraceStat} from './common';
-import {EngineProxy, LONG, NUM, Store, STR, STR_NULL} from '../../public';
+import {Engine, LONG, NUM, Store, STR, STR_NULL} from '../../public';
import {raf} from '../../core/raf_scheduler';
import {AsyncLimiter} from '../../base/async_limiter';
import {Monitor} from '../../base/monitor';
@@ -40,7 +40,7 @@
interface FtraceExplorerAttrs {
cache: FtraceExplorerCache;
filterStore: Store<FtraceFilter>;
- engine: EngineProxy;
+ engine: Engine;
}
interface FtraceEvent {
@@ -69,7 +69,7 @@
counters: FtraceStat[];
}
-async function getFtraceCounters(engine: EngineProxy): Promise<FtraceStat[]> {
+async function getFtraceCounters(engine: Engine): Promise<FtraceStat[]> {
// TODO(stevegolton): this is an extraordinarily slow query on large traces
// as it goes through every ftrace event which can be a lot on big traces.
// Consider if we can have some different UX which avoids needing these
@@ -264,7 +264,7 @@
}
async function lookupFtraceEvents(
- engine: EngineProxy,
+ engine: Engine,
offset: number,
count: number,
filter: FtraceFilter,
diff --git a/ui/src/core_plugins/ftrace/ftrace_track.ts b/ui/src/core_plugins/ftrace/ftrace_track.ts
index 6950eca..ed55cf6 100644
--- a/ui/src/core_plugins/ftrace/ftrace_track.ts
+++ b/ui/src/core_plugins/ftrace/ftrace_track.ts
@@ -20,7 +20,7 @@
import {globals} from '../../frontend/globals';
import {TrackData} from '../../common/track_data';
import {PanelSize} from '../../frontend/panel';
-import {EngineProxy, Track} from '../../public';
+import {Engine, Track} from '../../public';
import {LONG, STR} from '../../trace_processor/query_result';
import {FtraceFilter} from './common';
import {Store} from '../../public';
@@ -41,12 +41,12 @@
export class FtraceRawTrack implements Track {
private fetcher = new TimelineFetcher(this.onBoundsChange.bind(this));
- private engine: EngineProxy;
+ private engine: Engine;
private cpu: number;
private store: Store<FtraceFilter>;
private readonly monitor: Monitor;
- constructor(engine: EngineProxy, cpu: number, store: Store<FtraceFilter>) {
+ constructor(engine: Engine, cpu: number, store: Store<FtraceFilter>) {
this.engine = engine;
this.cpu = cpu;
this.store = store;
diff --git a/ui/src/core_plugins/ftrace/index.ts b/ui/src/core_plugins/ftrace/index.ts
index 3cc0616..9539c78 100644
--- a/ui/src/core_plugins/ftrace/index.ts
+++ b/ui/src/core_plugins/ftrace/index.ts
@@ -16,7 +16,7 @@
import {FtraceExplorer, FtraceExplorerCache} from './ftrace_explorer';
import {
- EngineProxy,
+ Engine,
Plugin,
PluginContextTrace,
PluginDescriptor,
@@ -108,7 +108,7 @@
this.trash.dispose();
}
- private async lookupCpuCores(engine: EngineProxy): Promise<number[]> {
+ private async lookupCpuCores(engine: Engine): Promise<number[]> {
const query = 'select distinct cpu from ftrace_event';
const result = await engine.query(query);
diff --git a/ui/src/core_plugins/perf_samples_profile/index.ts b/ui/src/core_plugins/perf_samples_profile/index.ts
index 752684a..319fb25 100644
--- a/ui/src/core_plugins/perf_samples_profile/index.ts
+++ b/ui/src/core_plugins/perf_samples_profile/index.ts
@@ -24,7 +24,7 @@
import {PanelSize} from '../../frontend/panel';
import {TimeScale} from '../../frontend/time_scale';
import {
- EngineProxy,
+ Engine,
Plugin,
PluginContextTrace,
PluginDescriptor,
@@ -50,9 +50,9 @@
private hoveredTs: time | undefined = undefined;
private fetcher = new TimelineFetcher(this.onBoundsChange.bind(this));
private upid: number;
- private engine: EngineProxy;
+ private engine: Engine;
- constructor(engine: EngineProxy, upid: number) {
+ constructor(engine: Engine, upid: number) {
this.upid = upid;
this.engine = engine;
}
diff --git a/ui/src/core_plugins/process_summary/process_scheduling_track.ts b/ui/src/core_plugins/process_summary/process_scheduling_track.ts
index 9725c29..28b536f 100644
--- a/ui/src/core_plugins/process_summary/process_scheduling_track.ts
+++ b/ui/src/core_plugins/process_summary/process_scheduling_track.ts
@@ -25,7 +25,7 @@
import {checkerboardExcept} from '../../frontend/checkerboard';
import {globals} from '../../frontend/globals';
import {PanelSize} from '../../frontend/panel';
-import {EngineProxy, Track} from '../../public';
+import {Engine, Track} from '../../public';
import {LONG, NUM, QueryResult} from '../../trace_processor/query_result';
import {uuidv4Sql} from '../../base/uuid';
@@ -57,11 +57,11 @@
private utidHoveredInThisTrack = -1;
private fetcher = new TimelineFetcher(this.onBoundsChange.bind(this));
private maxCpu = 0;
- private engine: EngineProxy;
+ private engine: Engine;
private trackUuid = uuidv4Sql();
private config: Config;
- constructor(engine: EngineProxy, config: Config) {
+ constructor(engine: Engine, config: Config) {
this.engine = engine;
this.config = config;
}
diff --git a/ui/src/core_plugins/process_summary/process_summary_track.ts b/ui/src/core_plugins/process_summary/process_summary_track.ts
index 7acf36b..5fa31de 100644
--- a/ui/src/core_plugins/process_summary/process_summary_track.ts
+++ b/ui/src/core_plugins/process_summary/process_summary_track.ts
@@ -19,11 +19,11 @@
import {duration, Time, time} from '../../base/time';
import {colorForTid} from '../../core/colorizer';
import {LIMIT, TrackData} from '../../common/track_data';
-import {EngineProxy, TimelineFetcher} from '../../common/track_helper';
+import {TimelineFetcher} from '../../common/track_helper';
import {checkerboardExcept} from '../../frontend/checkerboard';
import {globals} from '../../frontend/globals';
import {PanelSize} from '../../frontend/panel';
-import {Track} from '../../public';
+import {Engine, Track} from '../../public';
import {NUM} from '../../trace_processor/query_result';
export const PROCESS_SUMMARY_TRACK = 'ProcessSummaryTrack';
@@ -47,11 +47,11 @@
export class ProcessSummaryTrack implements Track {
private fetcher = new TimelineFetcher<Data>(this.onBoundsChange.bind(this));
- private engine: EngineProxy;
+ private engine: Engine;
private uuid = uuidv4();
private config: Config;
- constructor(engine: EngineProxy, config: Config) {
+ constructor(engine: Engine, config: Config) {
this.engine = engine;
this.config = config;
}
diff --git a/ui/src/core_plugins/sched/active_cpu_count.ts b/ui/src/core_plugins/sched/active_cpu_count.ts
index 721a884..2ab49bc 100644
--- a/ui/src/core_plugins/sched/active_cpu_count.ts
+++ b/ui/src/core_plugins/sched/active_cpu_count.ts
@@ -25,7 +25,7 @@
} from '../../frontend/base_counter_track';
import {CloseTrackButton} from '../../frontend/close_track_button';
import {globals} from '../../frontend/globals';
-import {EngineProxy, PrimaryTrackSortKey, TrackContext} from '../../public';
+import {Engine, PrimaryTrackSortKey, TrackContext} from '../../public';
export function addActiveCPUCountTrack(cpuType?: string) {
const cpuTypeName = cpuType === undefined ? '' : ` ${cpuType} `;
@@ -56,7 +56,7 @@
static readonly kind = 'dev.perfetto.Sched.ActiveCPUCount';
- constructor(ctx: TrackContext, engine: EngineProxy) {
+ constructor(ctx: TrackContext, engine: Engine) {
super({
engine,
trackKey: ctx.trackKey,
diff --git a/ui/src/core_plugins/screenshots/screenshot_panel.ts b/ui/src/core_plugins/screenshots/screenshot_panel.ts
index 066d14c..387ebf6 100644
--- a/ui/src/core_plugins/screenshots/screenshot_panel.ts
+++ b/ui/src/core_plugins/screenshots/screenshot_panel.ts
@@ -20,10 +20,10 @@
import {GenericSliceDetailsTabConfig} from '../../frontend/generic_slice_details_tab';
import {getSlice, SliceDetails} from '../../frontend/sql/slice';
import {asSliceSqlId} from '../../frontend/sql_types';
-import {EngineProxy} from '../../trace_processor/engine';
+import {Engine} from '../../trace_processor/engine';
async function getSliceDetails(
- engine: EngineProxy,
+ engine: Engine,
id: number,
): Promise<SliceDetails | undefined> {
return getSlice(engine, asSliceSqlId(id));
diff --git a/ui/src/frontend/app.ts b/ui/src/frontend/app.ts
index 92301b7..09b1036 100644
--- a/ui/src/frontend/app.ts
+++ b/ui/src/frontend/app.ts
@@ -32,7 +32,7 @@
} from '../core/timestamp_format';
import {raf} from '../core/raf_scheduler';
import {Command} from '../public';
-import {EngineProxy} from '../trace_processor/engine';
+import {Engine} from '../trace_processor/engine';
import {THREAD_STATE_TRACK_KIND} from '../core_plugins/thread_state';
import {HotkeyConfig, HotkeyContext} from '../widgets/hotkey_context';
import {HotkeyGlyphs} from '../widgets/hotkey_glyphs';
@@ -155,7 +155,7 @@
this.trash.add(new AggregationsTabs());
}
- private getEngine(): EngineProxy | undefined {
+ private getEngine(): Engine | undefined {
const engineId = globals.getCurrentEngine()?.id;
if (engineId === undefined) {
return undefined;
@@ -620,8 +620,8 @@
if (selection !== null && selection.kind === 'AREA') {
const area = globals.state.areas[selection.areaId];
const coversEntireTimeRange =
- globals.state.traceTime.start === area.start &&
- globals.state.traceTime.end === area.end;
+ globals.traceTime.start === area.start &&
+ globals.traceTime.end === area.end;
if (!coversEntireTimeRange) {
// If the current selection is an area which does not cover the
// entire time range, preserve the list of selected tracks and
@@ -636,7 +636,7 @@
// If the current selection is not an area, select all.
tracksToSelect = Object.keys(globals.state.tracks);
}
- const {start, end} = globals.state.traceTime;
+ const {start, end} = globals.traceTime;
globals.dispatch(
Actions.selectArea({
area: {
diff --git a/ui/src/frontend/base_counter_track.ts b/ui/src/frontend/base_counter_track.ts
index 990e6a3..9a37a6a 100644
--- a/ui/src/frontend/base_counter_track.ts
+++ b/ui/src/frontend/base_counter_track.ts
@@ -20,7 +20,7 @@
import {Time, time} from '../base/time';
import {drawTrackHoverTooltip} from '../common/canvas_utils';
import {raf} from '../core/raf_scheduler';
-import {EngineProxy, LONG, NUM, Track} from '../public';
+import {Engine, LONG, NUM, Track} from '../public';
import {Button} from '../widgets/button';
import {MenuItem, MenuDivider, PopupMenu2} from '../widgets/menu';
@@ -194,7 +194,7 @@
};
export abstract class BaseCounterTrack implements Track {
- protected engine: EngineProxy;
+ protected engine: Engine;
protected trackKey: string;
protected trackUuid = uuidv4Sql();
diff --git a/ui/src/frontend/base_slice_track.ts b/ui/src/frontend/base_slice_track.ts
index 7e013cc..720ace2 100644
--- a/ui/src/frontend/base_slice_track.ts
+++ b/ui/src/frontend/base_slice_track.ts
@@ -32,7 +32,7 @@
} from '../common/state';
import {featureFlags} from '../core/feature_flags';
import {raf} from '../core/raf_scheduler';
-import {EngineProxy, Slice, SliceRect, Track} from '../public';
+import {Engine, Slice, SliceRect, Track} from '../public';
import {LONG, NUM} from '../trace_processor/query_result';
import {checkerboardExcept} from './checkerboard';
@@ -175,7 +175,7 @@
> implements Track
{
protected sliceLayout: SliceLayout = {...DEFAULT_SLICE_LAYOUT};
- protected engine: EngineProxy;
+ protected engine: Engine;
protected trackKey: string;
protected trackUuid = uuidv4Sql();
diff --git a/ui/src/frontend/bottom_tab.ts b/ui/src/frontend/bottom_tab.ts
index d8ed117..9868a60 100644
--- a/ui/src/frontend/bottom_tab.ts
+++ b/ui/src/frontend/bottom_tab.ts
@@ -14,10 +14,10 @@
import m from 'mithril';
-import {EngineProxy} from '../trace_processor/engine';
+import {Engine} from '../trace_processor/engine';
export interface NewBottomTabArgs<Config> {
- engine: EngineProxy;
+ engine: Engine;
tag?: string;
uuid: string;
config: Config;
@@ -43,7 +43,7 @@
// Config for this details panel. Should be serializable.
protected readonly config: Config;
// Engine for running queries and fetching additional data.
- protected readonly engine: EngineProxy;
+ protected readonly engine: Engine;
// Optional tag, which is used to ensure that only one tab
// with the same tag can exist - adding a new tab with the same tag
// (e.g. 'current_selection') would close the previous one. This
diff --git a/ui/src/frontend/chrome_slice_details_tab.ts b/ui/src/frontend/chrome_slice_details_tab.ts
index 49196fa..91cf54f 100644
--- a/ui/src/frontend/chrome_slice_details_tab.ts
+++ b/ui/src/frontend/chrome_slice_details_tab.ts
@@ -19,7 +19,7 @@
import {exists} from '../base/utils';
import {runQuery} from '../common/queries';
import {raf} from '../core/raf_scheduler';
-import {EngineProxy} from '../trace_processor/engine';
+import {Engine} from '../trace_processor/engine';
import {LONG, LONG_NULL, NUM, STR_NULL} from '../trace_processor/query_result';
import {Button} from '../widgets/button';
import {DetailsShell} from '../widgets/details_shell';
@@ -167,7 +167,7 @@
return ITEMS.filter((item) => item.shouldDisplay(slice));
}
-function getEngine(): EngineProxy | undefined {
+function getEngine(): Engine | undefined {
const engineId = globals.getCurrentEngine()?.id;
if (engineId === undefined) {
return undefined;
@@ -177,7 +177,7 @@
}
async function getAnnotationSlice(
- engine: EngineProxy,
+ engine: Engine,
id: number,
): Promise<SliceDetails | undefined> {
const query = await engine.query(`
@@ -218,7 +218,7 @@
}
async function getSliceDetails(
- engine: EngineProxy,
+ engine: Engine,
id: number,
table: string,
): Promise<SliceDetails | undefined> {
@@ -300,7 +300,7 @@
return !exists(this.sliceDetails);
}
- private renderRhs(engine: EngineProxy, slice: SliceDetails): m.Children {
+ private renderRhs(engine: Engine, slice: SliceDetails): m.Children {
const precFlows = this.renderPrecedingFlows(slice);
const followingFlows = this.renderFollowingFlows(slice);
const args =
diff --git a/ui/src/frontend/debug_tracks.ts b/ui/src/frontend/debug_tracks.ts
index ed77bf0..7b3fe11 100644
--- a/ui/src/frontend/debug_tracks.ts
+++ b/ui/src/frontend/debug_tracks.ts
@@ -16,7 +16,7 @@
import {Actions, DeferredAction} from '../common/actions';
import {SCROLLING_TRACK_GROUP} from '../common/state';
import {globals} from './globals';
-import {EngineProxy, PrimaryTrackSortKey} from '../public';
+import {Engine, PrimaryTrackSortKey} from '../public';
import {DebugTrackV2Config} from '../core_plugins/debug/slice_track';
export const ARG_PREFIX = 'arg_';
@@ -55,7 +55,7 @@
// once or want to tweak the actions once produced. Otherwise, use
// addDebugSliceTrack().
export async function createDebugSliceTrackActions(
- _engine: EngineProxy,
+ _engine: Engine,
data: SqlDataSource,
trackName: string,
sliceColumns: SliceColumns,
@@ -90,7 +90,7 @@
}
export async function addPivotDebugSliceTracks(
- engine: EngineProxy,
+ engine: Engine,
data: SqlDataSource,
trackName: string,
sliceColumns: SliceColumns,
@@ -129,7 +129,7 @@
// Adds a debug track immediately. Use createDebugSliceTrackActions() if you
// want to create many tracks at once.
export async function addDebugSliceTrack(
- engine: EngineProxy,
+ engine: Engine,
data: SqlDataSource,
trackName: string,
sliceColumns: SliceColumns,
diff --git a/ui/src/frontend/globals.ts b/ui/src/frontend/globals.ts
index 50b2286..04cdc17 100644
--- a/ui/src/frontend/globals.ts
+++ b/ui/src/frontend/globals.ts
@@ -44,7 +44,7 @@
import {TrackManager} from '../common/track_cache';
import {setPerfHooks} from '../core/perf';
import {raf} from '../core/raf_scheduler';
-import {Engine} from '../trace_processor/engine';
+import {EngineBase} from '../trace_processor/engine';
import {HttpRpcState} from '../trace_processor/http_rpc_engine';
import {Analytics, initAnalytics} from './analytics';
@@ -221,6 +221,32 @@
pendingScrollId: number | undefined;
}
+export interface TraceTime {
+ readonly start: time;
+ readonly end: time;
+
+ // This is the ts value at the time of the Unix epoch.
+ // Normally some large negative value, because the unix epoch is normally in
+ // the past compared to ts=0.
+ readonly realtimeOffset: time;
+
+ // This is the timestamp that we should use for our offset when in UTC mode.
+ // Usually the most recent UTC midnight compared to the trace start time.
+ readonly utcOffset: time;
+
+ // Trace TZ is like UTC but keeps into account also the timezone_off_mins
+ // recorded into the trace, to show timestamps in the device local time.
+ readonly traceTzOffset: time;
+}
+
+export const defaultTraceTime: TraceTime = {
+ start: Time.ZERO,
+ end: Time.fromSeconds(10),
+ realtimeOffset: Time.ZERO,
+ utcOffset: Time.ZERO,
+ traceTzOffset: Time.ZERO,
+};
+
/**
* Global accessors for state/dispatch in the frontend.
*/
@@ -260,9 +286,6 @@
private _embeddedMode?: boolean = undefined;
private _hideSidebar?: boolean = undefined;
private _cmdManager = new CommandManager();
- private _realtimeOffset = Time.ZERO;
- private _utcOffset = Time.ZERO;
- private _traceTzOffset = Time.ZERO;
private _tabManager = new TabManager();
private _trackManager = new TrackManager(this._store);
private _selectionManager = new SelectionManager(this._store);
@@ -273,6 +296,8 @@
newVersionAvailable = false;
showPanningHint = false;
+ traceTime = defaultTraceTime;
+
// TODO(hjd): Remove once we no longer need to update UUID on redraw.
private _publishRedraw?: () => void = undefined;
@@ -290,7 +315,7 @@
count: new Uint8Array(0),
};
- engines = new Map<string, Engine>();
+ engines = new Map<string, EngineBase>();
initialize(dispatch: Dispatch, router: Router) {
this._dispatch = dispatch;
@@ -691,19 +716,19 @@
// Get a timescale that covers the entire trace
getTraceTimeScale(pxSpan: PxSpan): TimeScale {
- const {start, end} = this.state.traceTime;
+ const {start, end} = this.traceTime;
const traceTime = HighPrecisionTimeSpan.fromTime(start, end);
return TimeScale.fromHPTimeSpan(traceTime, pxSpan);
}
// Get the trace time bounds
stateTraceTime(): Span<HighPrecisionTime> {
- const {start, end} = this.state.traceTime;
+ const {start, end} = this.traceTime;
return HighPrecisionTimeSpan.fromTime(start, end);
}
stateTraceTimeTP(): Span<time, duration> {
- const {start, end} = this.state.traceTime;
+ const {start, end} = this.traceTime;
return new TimeSpan(start, end);
}
@@ -723,37 +748,6 @@
return assertExists(this._cmdManager);
}
- // This is the ts value at the time of the Unix epoch.
- // Normally some large negative value, because the unix epoch is normally in
- // the past compared to ts=0.
- get realtimeOffset(): time {
- return this._realtimeOffset;
- }
-
- set realtimeOffset(time: time) {
- this._realtimeOffset = time;
- }
-
- // This is the timestamp that we should use for our offset when in UTC mode.
- // Usually the most recent UTC midnight compared to the trace start time.
- get utcOffset(): time {
- return this._utcOffset;
- }
-
- set utcOffset(offset: time) {
- this._utcOffset = offset;
- }
-
- // Trace TZ is like UTC but keeps into account also the timezone_off_mins
- // recorded into the trace, to show timestamps in the device local time.
- get traceTzOffset(): time {
- return this._traceTzOffset;
- }
-
- set traceTzOffset(offset: time) {
- this._traceTzOffset = offset;
- }
-
get tabManager() {
return this._tabManager;
}
@@ -768,14 +762,14 @@
switch (fmt) {
case TimestampFormat.Timecode:
case TimestampFormat.Seconds:
- return this.state.traceTime.start;
+ return this.traceTime.start;
case TimestampFormat.Raw:
case TimestampFormat.RawLocale:
return Time.ZERO;
case TimestampFormat.UTC:
- return this.utcOffset;
+ return this.traceTime.utcOffset;
case TimestampFormat.TraceTz:
- return this.traceTzOffset;
+ return this.traceTime.traceTzOffset;
default:
const x: never = fmt;
throw new Error(`Unsupported format ${x}`);
diff --git a/ui/src/frontend/metrics_page.ts b/ui/src/frontend/metrics_page.ts
index 6b7ffce..124cb84 100644
--- a/ui/src/frontend/metrics_page.ts
+++ b/ui/src/frontend/metrics_page.ts
@@ -25,7 +25,7 @@
import {pluginManager, PluginManager} from '../common/plugins';
import {raf} from '../core/raf_scheduler';
import {MetricVisualisation} from '../public';
-import {EngineProxy} from '../trace_processor/engine';
+import {Engine} from '../trace_processor/engine';
import {STR} from '../trace_processor/query_result';
import {Select} from '../widgets/select';
import {Spinner} from '../widgets/spinner';
@@ -37,7 +37,7 @@
type Format = 'json' | 'prototext' | 'proto';
const FORMATS: Format[] = ['json', 'prototext', 'proto'];
-function getEngine(): EngineProxy | undefined {
+function getEngine(): Engine | undefined {
const engineId = globals.getCurrentEngine()?.id;
if (engineId === undefined) {
return undefined;
@@ -46,7 +46,7 @@
return engine;
}
-async function getMetrics(engine: EngineProxy): Promise<string[]> {
+async function getMetrics(engine: Engine): Promise<string[]> {
const metrics: string[] = [];
const metricsResult = await engine.query('select name from trace_metrics');
for (const it = metricsResult.iter({name: STR}); it.valid(); it.next()) {
@@ -56,7 +56,7 @@
}
async function getMetric(
- engine: EngineProxy,
+ engine: Engine,
metric: string,
format: Format,
): Promise<string> {
@@ -69,7 +69,7 @@
}
class MetricsController {
- engine: EngineProxy;
+ engine: Engine;
plugins: PluginManager;
private _metrics: string[];
private _selected?: string;
@@ -78,7 +78,7 @@
// eslint-disable-next-line @typescript-eslint/no-explicit-any
private _json: any;
- constructor(plugins: PluginManager, engine: EngineProxy) {
+ constructor(plugins: PluginManager, engine: Engine) {
this.plugins = plugins;
this.engine = engine;
this._metrics = [];
diff --git a/ui/src/frontend/publish.ts b/ui/src/frontend/publish.ts
index f8c36cb..bfb34ca 100644
--- a/ui/src/frontend/publish.ts
+++ b/ui/src/frontend/publish.ts
@@ -12,7 +12,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-import {time} from '../base/time';
import {Actions} from '../common/actions';
import {AggregateData} from '../common/aggregation_data';
import {ConversionJobStatusUpdate} from '../common/conversion_jobs';
@@ -32,6 +31,7 @@
SliceDetails,
ThreadDesc,
ThreadStateDetails,
+ TraceTime,
} from './globals';
import {findCurrentSelection} from './keyboard_event_handler';
@@ -96,14 +96,8 @@
globals.publishRedraw();
}
-export function publishRealtimeOffset(
- offset: time,
- utcOffset: time,
- traceTzOffset: time,
-) {
- globals.realtimeOffset = offset;
- globals.utcOffset = utcOffset;
- globals.traceTzOffset = traceTzOffset;
+export function publishTraceDetails(details: TraceTime): void {
+ globals.traceTime = details;
globals.publishRedraw();
}
diff --git a/ui/src/frontend/query_page.ts b/ui/src/frontend/query_page.ts
index 7afcbaf..80b8858 100644
--- a/ui/src/frontend/query_page.ts
+++ b/ui/src/frontend/query_page.ts
@@ -19,7 +19,7 @@
import {undoCommonChatAppReplacements} from '../base/string_utils';
import {QueryResponse, runQuery} from '../common/queries';
import {raf} from '../core/raf_scheduler';
-import {EngineProxy} from '../trace_processor/engine';
+import {Engine} from '../trace_processor/engine';
import {Callout} from '../widgets/callout';
import {Editor} from '../widgets/editor';
@@ -71,7 +71,7 @@
raf.scheduleDelayedFullRedraw();
}
-function getEngine(): EngineProxy | undefined {
+function getEngine(): Engine | undefined {
const engineId = globals.getCurrentEngine()?.id;
if (engineId === undefined) {
return undefined;
diff --git a/ui/src/frontend/query_result_tab.ts b/ui/src/frontend/query_result_tab.ts
index 7fe7b2d..e6feda3 100644
--- a/ui/src/frontend/query_result_tab.ts
+++ b/ui/src/frontend/query_result_tab.ts
@@ -32,7 +32,7 @@
import {globals} from './globals';
import {Actions} from '../common/actions';
import {BottomTabToTabAdapter} from '../public/utils';
-import {EngineProxy} from '../public';
+import {Engine} from '../public';
interface QueryResultTabConfig {
readonly query: string;
@@ -66,7 +66,7 @@
}
// TODO(stevegolton): Find a way to make this more elegant.
-function getEngine(): EngineProxy {
+function getEngine(): Engine {
const engConfig = globals.getCurrentEngine();
const engineId = assertExists(engConfig).id;
return assertExists(globals.engines.get(engineId)).getProxy('QueryResult');
diff --git a/ui/src/frontend/sidebar.ts b/ui/src/frontend/sidebar.ts
index be1d20e..1cedf6a 100644
--- a/ui/src/frontend/sidebar.ts
+++ b/ui/src/frontend/sidebar.ts
@@ -29,7 +29,7 @@
import {featureFlags} from '../core/feature_flags';
import {raf} from '../core/raf_scheduler';
import {SCM_REVISION, VERSION} from '../gen/perfetto_version';
-import {Engine} from '../trace_processor/engine';
+import {EngineBase} from '../trace_processor/engine';
import {showModal} from '../widgets/modal';
import {Animation} from './animation';
@@ -566,7 +566,7 @@
downloadUrl(fileName, url);
}
-function getCurrentEngine(): Engine | undefined {
+function getCurrentEngine(): EngineBase | undefined {
const engineId = globals.getCurrentEngine()?.id;
if (engineId === undefined) return undefined;
return globals.engines.get(engineId);
diff --git a/ui/src/frontend/simple_counter_track.ts b/ui/src/frontend/simple_counter_track.ts
index 361480b..5b21ded 100644
--- a/ui/src/frontend/simple_counter_track.ts
+++ b/ui/src/frontend/simple_counter_track.ts
@@ -13,7 +13,7 @@
// limitations under the License.
import m from 'mithril';
-import {EngineProxy, TrackContext} from '../public';
+import {Engine, TrackContext} from '../public';
import {BaseCounterTrack, CounterOptions} from './base_counter_track';
import {CounterColumns, SqlDataSource} from './debug_tracks';
import {Disposable, DisposableCallback} from '../base/disposable';
@@ -30,7 +30,7 @@
private sqlTableName: string;
constructor(
- engine: EngineProxy,
+ engine: Engine,
ctx: TrackContext,
config: SimpleCounterTrackConfig,
) {
diff --git a/ui/src/frontend/simple_slice_track.ts b/ui/src/frontend/simple_slice_track.ts
index f46812f..c292fe6 100644
--- a/ui/src/frontend/simple_slice_track.ts
+++ b/ui/src/frontend/simple_slice_track.ts
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-import {EngineProxy, TrackContext} from '../public';
+import {Engine, TrackContext} from '../public';
import {
CustomSqlDetailsPanelConfig,
CustomSqlTableDefConfig,
@@ -35,7 +35,7 @@
private sqlTableName: string;
constructor(
- engine: EngineProxy,
+ engine: Engine,
ctx: TrackContext,
config: SimpleSliceTrackConfig,
) {
diff --git a/ui/src/frontend/slice_args.ts b/ui/src/frontend/slice_args.ts
index 97b3b6f..ec9d609 100644
--- a/ui/src/frontend/slice_args.ts
+++ b/ui/src/frontend/slice_args.ts
@@ -22,7 +22,7 @@
import {Actions, AddTrackArgs} from '../common/actions';
import {InThreadTrackSortKey} from '../common/state';
import {ArgNode, convertArgsToTree, Key} from '../controller/args_parser';
-import {EngineProxy} from '../trace_processor/engine';
+import {Engine} from '../trace_processor/engine';
import {NUM} from '../trace_processor/query_result';
import {
VISUALISED_ARGS_SLICE_TRACK_URI,
@@ -39,7 +39,7 @@
import {assertExists} from '../base/logging';
// Renders slice arguments (key/value pairs) as a subtree.
-export function renderArguments(engine: EngineProxy, args: Arg[]): m.Children {
+export function renderArguments(engine: Engine, args: Arg[]): m.Children {
if (args.length > 0) {
const tree = convertArgsToTree(args);
return renderArgTreeNodes(engine, tree);
@@ -52,10 +52,7 @@
return exists(args) && args.length > 0;
}
-function renderArgTreeNodes(
- engine: EngineProxy,
- args: ArgNode<Arg>[],
-): m.Children {
+function renderArgTreeNodes(engine: Engine, args: ArgNode<Arg>[]): m.Children {
return args.map((arg) => {
const {key, value, children} = arg;
if (children && children.length === 1) {
@@ -80,11 +77,7 @@
});
}
-function renderArgKey(
- engine: EngineProxy,
- key: string,
- value?: Arg,
-): m.Children {
+function renderArgKey(engine: Engine, key: string, value?: Arg): m.Children {
if (value === undefined) {
return key;
} else {
@@ -125,7 +118,7 @@
}
}
-async function addVisualisedArg(engine: EngineProxy, argName: string) {
+async function addVisualisedArg(engine: Engine, argName: string) {
const escapedArgName = argName.replace(/[^a-zA-Z]/g, '_');
const tableName = `__arg_visualisation_helper_${escapedArgName}_slice`;
diff --git a/ui/src/frontend/sql/args.ts b/ui/src/frontend/sql/args.ts
index 2cf051c..f36f253 100644
--- a/ui/src/frontend/sql/args.ts
+++ b/ui/src/frontend/sql/args.ts
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-import {EngineProxy} from '../../trace_processor/engine';
+import {Engine} from '../../trace_processor/engine';
import {
LONG_NULL,
NUM,
@@ -42,7 +42,7 @@
}
export async function getArgs(
- engine: EngineProxy,
+ engine: Engine,
argSetId: ArgSetId,
): Promise<Arg[]> {
const query = await engine.query(`
diff --git a/ui/src/frontend/sql/details/details.ts b/ui/src/frontend/sql/details/details.ts
index 42b500d..d244747 100644
--- a/ui/src/frontend/sql/details/details.ts
+++ b/ui/src/frontend/sql/details/details.ts
@@ -18,7 +18,7 @@
import {Time} from '../../../base/time';
import {exists} from '../../../base/utils';
import {raf} from '../../../core/raf_scheduler';
-import {EngineProxy} from '../../../public';
+import {Engine} from '../../../public';
import {Row, SqlValue} from '../../../trace_processor/query_result';
import {Anchor} from '../../../widgets/anchor';
import {renderError} from '../../../widgets/error';
@@ -202,7 +202,7 @@
// Class responsible for fetching the data and rendering the data.
export class Details {
constructor(
- private engine: EngineProxy,
+ private engine: Engine,
private sqlTable: string,
private id: number,
schema: {[key: string]: ValueDesc},
@@ -278,14 +278,14 @@
// async `fetch` step for fetching data and sync `render` step for generating
// the vdom.
export type SqlIdRefRenderer = {
- fetch: (engine: EngineProxy, id: bigint) => Promise<{} | undefined>;
+ fetch: (engine: Engine, id: bigint) => Promise<{} | undefined>;
render: (data: {}) => RenderedValue;
};
// Type-safe helper to create a SqlIdRefRenderer, which ensures that the
// type returned from the fetch is the same type that renderer takes.
export function createSqlIdRefRenderer<Data extends {}>(
- fetch: (engine: EngineProxy, id: bigint) => Promise<Data>,
+ fetch: (engine: Engine, id: bigint) => Promise<Data>,
render: (data: Data) => RenderedValue,
): SqlIdRefRenderer {
return {fetch, render: render as (data: {}) => RenderedValue};
@@ -451,7 +451,7 @@
data?: Data;
constructor(
- private engine: EngineProxy,
+ private engine: Engine,
private sqlTable: string,
private id: number,
public sqlIdRefRenderers: {[table: string]: SqlIdRefRenderer},
@@ -652,7 +652,7 @@
// Generate the vdom for a given value using the fetched `data`.
function renderValue(
- engine: EngineProxy,
+ engine: Engine,
key: string,
value: ResolvedValue,
data: Data,
diff --git a/ui/src/frontend/sql/slice.ts b/ui/src/frontend/sql/slice.ts
index 050ec4e..bf35774 100644
--- a/ui/src/frontend/sql/slice.ts
+++ b/ui/src/frontend/sql/slice.ts
@@ -18,7 +18,7 @@
import {Icons} from '../../base/semantic_icons';
import {duration, Time, time} from '../../base/time';
import {exists} from '../../base/utils';
-import {EngineProxy} from '../../trace_processor/engine';
+import {Engine} from '../../trace_processor/engine';
import {
LONG,
LONG_NULL,
@@ -68,7 +68,7 @@
}
async function getUtidAndUpid(
- engine: EngineProxy,
+ engine: Engine,
sqlTrackId: number,
): Promise<{utid?: Utid; upid?: Upid}> {
const columnInfo = (
@@ -118,7 +118,7 @@
}
export async function getSliceFromConstraints(
- engine: EngineProxy,
+ engine: Engine,
constraints: SQLConstraints,
): Promise<SliceDetails[]> {
const query = await engine.query(`
@@ -186,7 +186,7 @@
}
export async function getSlice(
- engine: EngineProxy,
+ engine: Engine,
id: SliceSqlId,
): Promise<SliceDetails | undefined> {
const result = await getSliceFromConstraints(engine, {
@@ -272,7 +272,7 @@
// Get all descendants for a given slice in a tree form.
export async function getDescendantSliceTree(
- engine: EngineProxy,
+ engine: Engine,
id: SliceSqlId,
): Promise<SliceTreeNode | undefined> {
const slice = await getSlice(engine, id);
diff --git a/ui/src/frontend/sql/thread_state.ts b/ui/src/frontend/sql/thread_state.ts
index e47a5c1..7009a6f 100644
--- a/ui/src/frontend/sql/thread_state.ts
+++ b/ui/src/frontend/sql/thread_state.ts
@@ -15,7 +15,7 @@
import m from 'mithril';
import {duration, TimeSpan} from '../../base/time';
-import {EngineProxy} from '../../public';
+import {Engine} from '../../public';
import {
LONG,
NUM_NULL,
@@ -67,7 +67,7 @@
// Compute a breakdown of thread states for a given thread for a given time
// interval.
export async function breakDownIntervalByThreadState(
- engine: EngineProxy,
+ engine: Engine,
range: TimeSpan,
utid: Utid,
): Promise<BreakdownByThreadState> {
diff --git a/ui/src/frontend/sql_table/argument_selector.ts b/ui/src/frontend/sql_table/argument_selector.ts
index 29b4281..039cc0a 100644
--- a/ui/src/frontend/sql_table/argument_selector.ts
+++ b/ui/src/frontend/sql_table/argument_selector.ts
@@ -15,7 +15,7 @@
import m from 'mithril';
import {raf} from '../../core/raf_scheduler';
-import {EngineProxy} from '../../trace_processor/engine';
+import {Engine} from '../../trace_processor/engine';
import {STR} from '../../trace_processor/query_result';
import {FilterableSelect} from '../../widgets/select';
import {Spinner} from '../../widgets/spinner';
@@ -31,7 +31,7 @@
const MAX_ARGS_TO_DISPLAY = 15;
interface ArgumentSelectorAttrs {
- engine: EngineProxy;
+ engine: Engine;
argSetId: ArgSetIdColumn;
tableName: string;
constraints: SQLConstraints;
diff --git a/ui/src/frontend/sql_table/state.ts b/ui/src/frontend/sql_table/state.ts
index 96118f5..2093dad 100644
--- a/ui/src/frontend/sql_table/state.ts
+++ b/ui/src/frontend/sql_table/state.ts
@@ -17,7 +17,7 @@
import {isString} from '../../base/object_utils';
import {sqliteString} from '../../base/string_utils';
import {raf} from '../../core/raf_scheduler';
-import {EngineProxy} from '../../trace_processor/engine';
+import {Engine} from '../../trace_processor/engine';
import {NUM, Row} from '../../trace_processor/query_result';
import {
constraintsToQueryPrefix,
@@ -76,7 +76,7 @@
}
export class SqlTableState {
- private readonly engine_: EngineProxy;
+ private readonly engine_: Engine;
private readonly table_: SqlTableDescription;
private readonly additionalImports: string[];
@@ -95,7 +95,7 @@
private rowCount?: RowCount;
constructor(
- engine: EngineProxy,
+ engine: Engine,
table: SqlTableDescription,
filters?: Filter[],
imports?: string[],
diff --git a/ui/src/frontend/sql_table/state_unittest.ts b/ui/src/frontend/sql_table/state_unittest.ts
index 91d96a2..ebb20b5 100644
--- a/ui/src/frontend/sql_table/state_unittest.ts
+++ b/ui/src/frontend/sql_table/state_unittest.ts
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-import {Engine, EngineProxy} from '../../trace_processor/engine';
+import {EngineBase} from '../../trace_processor/engine';
import {Column} from './column';
import {SqlTableState} from './state';
@@ -44,14 +44,14 @@
],
};
-class FakeEngine extends Engine {
+class FakeEngine extends EngineBase {
id: string = 'TestEngine';
rpcSendRequestBytes(_data: Uint8Array) {}
}
test('sqlTableState: columnManupulation', () => {
- const engine = new EngineProxy(new FakeEngine(), 'test');
+ const engine = new FakeEngine();
const state = new SqlTableState(engine, table);
const idColumn = {
@@ -88,7 +88,7 @@
});
test('sqlTableState: sortedColumns', () => {
- const engine = new EngineProxy(new FakeEngine(), 'test');
+ const engine = new FakeEngine();
const state = new SqlTableState(engine, table);
// Verify that we have two columns: "id" and "name" and
@@ -139,7 +139,7 @@
}
test('sqlTableState: sqlStatement', () => {
- const engine = new EngineProxy(new FakeEngine(), 'test');
+ const engine = new FakeEngine();
const state = new SqlTableState(engine, table);
// Check the generated SQL statement.
diff --git a/ui/src/frontend/sql_table/tab.ts b/ui/src/frontend/sql_table/tab.ts
index 4267331..a91eee4 100644
--- a/ui/src/frontend/sql_table/tab.ts
+++ b/ui/src/frontend/sql_table/tab.ts
@@ -25,7 +25,7 @@
import {Filter, SqlTableState} from './state';
import {SqlTable} from './table';
import {SqlTableDescription, tableDisplayName} from './table_description';
-import {EngineProxy} from '../../public';
+import {Engine} from '../../public';
import {globals} from '../globals';
import {assertExists} from '../../base/logging';
import {uuidv4} from '../../base/uuid';
@@ -58,7 +58,7 @@
}
// TODO(stevegolton): Find a way to make this more elegant.
-function getEngine(): EngineProxy {
+function getEngine(): Engine {
const engConfig = globals.getCurrentEngine();
const engineId = assertExists(engConfig).id;
return assertExists(globals.engines.get(engineId)).getProxy('QueryResult');
diff --git a/ui/src/frontend/sql_table/table.ts b/ui/src/frontend/sql_table/table.ts
index d0db696..35e620f 100644
--- a/ui/src/frontend/sql_table/table.ts
+++ b/ui/src/frontend/sql_table/table.ts
@@ -16,7 +16,7 @@
import {isString} from '../../base/object_utils';
import {Icons} from '../../base/semantic_icons';
-import {EngineProxy} from '../../trace_processor/engine';
+import {Engine} from '../../trace_processor/engine';
import {Row} from '../../trace_processor/query_result';
import {Anchor} from '../../widgets/anchor';
import {BasicTable} from '../../widgets/basic_table';
@@ -37,7 +37,7 @@
export class SqlTable implements m.ClassComponent<SqlTableConfig> {
private readonly table: SqlTableDescription;
- private readonly engine: EngineProxy;
+ private readonly engine: Engine;
private state: SqlTableState;
diff --git a/ui/src/frontend/sql_utils.ts b/ui/src/frontend/sql_utils.ts
index 891a876..a78a88a 100644
--- a/ui/src/frontend/sql_utils.ts
+++ b/ui/src/frontend/sql_utils.ts
@@ -14,7 +14,7 @@
import {isString} from '../base/object_utils';
import {SortDirection} from '../common/state';
-import {EngineProxy} from '../trace_processor/engine';
+import {Engine} from '../trace_processor/engine';
import {ColumnType, NUM} from '../trace_processor/query_result';
export interface OrderClause {
@@ -111,7 +111,7 @@
}
export async function getTableRowCount(
- engine: EngineProxy,
+ engine: Engine,
tableName: string,
): Promise<number | undefined> {
const result = await engine.query(
diff --git a/ui/src/frontend/thread_and_process_info.ts b/ui/src/frontend/thread_and_process_info.ts
index b54cb45..2e70b29 100644
--- a/ui/src/frontend/thread_and_process_info.ts
+++ b/ui/src/frontend/thread_and_process_info.ts
@@ -17,7 +17,7 @@
import {copyToClipboard} from '../base/clipboard';
import {Icons} from '../base/semantic_icons';
import {exists} from '../base/utils';
-import {EngineProxy} from '../trace_processor/engine';
+import {Engine} from '../trace_processor/engine';
import {NUM, NUM_NULL, STR, STR_NULL} from '../trace_processor/query_result';
import {Anchor} from '../widgets/anchor';
import {MenuItem, PopupMenu2} from '../widgets/menu';
@@ -43,7 +43,7 @@
}
export async function getProcessInfo(
- engine: EngineProxy,
+ engine: Engine,
upid: Upid,
): Promise<ProcessInfo> {
const it = (
@@ -137,7 +137,7 @@
}
export async function getThreadInfo(
- engine: EngineProxy,
+ engine: Engine,
utid: Utid,
): Promise<ThreadInfo> {
const it = (
diff --git a/ui/src/frontend/thread_state.ts b/ui/src/frontend/thread_state.ts
index e912a7e..8a3d0ec 100644
--- a/ui/src/frontend/thread_state.ts
+++ b/ui/src/frontend/thread_state.ts
@@ -19,7 +19,7 @@
import {exists} from '../base/utils';
import {Actions} from '../common/actions';
import {translateState} from '../common/thread_state';
-import {EngineProxy} from '../trace_processor/engine';
+import {Engine} from '../trace_processor/engine';
import {LONG, NUM, NUM_NULL, STR_NULL} from '../trace_processor/query_result';
import {CPU_SLICE_TRACK_KIND} from '../core_plugins/cpu_slices';
import {THREAD_STATE_TRACK_KIND} from '../core_plugins/thread_state';
@@ -59,7 +59,7 @@
// Gets a list of thread state objects from Trace Processor with given
// constraints.
export async function getThreadStateFromConstraints(
- engine: EngineProxy,
+ engine: Engine,
constraints: SQLConstraints,
): Promise<ThreadState[]> {
const query = await engine.query(`
@@ -120,7 +120,7 @@
}
export async function getThreadState(
- engine: EngineProxy,
+ engine: Engine,
id: number,
): Promise<ThreadState | undefined> {
const result = await getThreadStateFromConstraints(engine, {
diff --git a/ui/src/frontend/time_axis_panel.ts b/ui/src/frontend/time_axis_panel.ts
index d20341f..af1f3df 100644
--- a/ui/src/frontend/time_axis_panel.ts
+++ b/ui/src/frontend/time_axis_panel.ts
@@ -57,16 +57,16 @@
break;
case TimestampFormat.UTC:
const offsetDate = Time.toDate(
- globals.utcOffset,
- globals.realtimeOffset,
+ globals.traceTime.utcOffset,
+ globals.traceTime.realtimeOffset,
);
const dateStr = toISODateOnly(offsetDate);
ctx.fillText(`UTC ${dateStr}`, 6, 10);
break;
case TimestampFormat.TraceTz:
const offsetTzDate = Time.toDate(
- globals.traceTzOffset,
- globals.realtimeOffset,
+ globals.traceTime.traceTzOffset,
+ globals.traceTime.realtimeOffset,
);
const dateTzStr = toISODateOnly(offsetTzDate);
ctx.fillText(dateTzStr, 6, 10);
diff --git a/ui/src/frontend/trace_info_page.ts b/ui/src/frontend/trace_info_page.ts
index 485146a..c8d7547 100644
--- a/ui/src/frontend/trace_info_page.ts
+++ b/ui/src/frontend/trace_info_page.ts
@@ -16,7 +16,7 @@
import {QueryResponse, runQuery} from '../common/queries';
import {raf} from '../core/raf_scheduler';
-import {EngineProxy} from '../trace_processor/engine';
+import {Engine} from '../trace_processor/engine';
import {globals} from './globals';
import {createPage} from './pages';
@@ -29,7 +29,7 @@
queryId: string;
}
-function getEngine(name: string): EngineProxy | undefined {
+function getEngine(name: string): Engine | undefined {
const currentEngine = globals.getCurrentEngine();
if (currentEngine === undefined) return undefined;
const engineId = currentEngine.id;
diff --git a/ui/src/frontend/track.ts b/ui/src/frontend/track.ts
index cd11ce3..c6462b0 100644
--- a/ui/src/frontend/track.ts
+++ b/ui/src/frontend/track.ts
@@ -12,9 +12,9 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-import {EngineProxy} from '../trace_processor/engine';
+import {Engine} from '../trace_processor/engine';
export interface NewTrackArgs {
trackKey: string;
- engine: EngineProxy;
+ engine: Engine;
}
diff --git a/ui/src/frontend/viewer_page.ts b/ui/src/frontend/viewer_page.ts
index b599dc7..b67f2e5 100644
--- a/ui/src/frontend/viewer_page.ts
+++ b/ui/src/frontend/viewer_page.ts
@@ -128,7 +128,7 @@
currentY: number,
editing: boolean,
) => {
- const traceTime = globals.state.traceTime;
+ const traceTime = globals.traceTime;
const {visibleTimeScale} = timeline;
this.keepCurrentSelection = true;
if (editing) {
diff --git a/ui/src/frontend/viz_page.ts b/ui/src/frontend/viz_page.ts
index dadb34a..6145763 100644
--- a/ui/src/frontend/viz_page.ts
+++ b/ui/src/frontend/viz_page.ts
@@ -15,14 +15,14 @@
import m from 'mithril';
import {raf} from '../core/raf_scheduler';
-import {EngineProxy} from '../trace_processor/engine';
+import {Engine} from '../trace_processor/engine';
import {Editor} from '../widgets/editor';
import {VegaView} from '../widgets/vega_view';
import {globals} from './globals';
import {createPage} from './pages';
-function getEngine(): EngineProxy | undefined {
+function getEngine(): Engine | undefined {
const engineId = globals.getCurrentEngine()?.id;
if (engineId === undefined) {
return undefined;
@@ -32,7 +32,7 @@
}
let SPEC = '';
-let ENGINE: EngineProxy | undefined = undefined;
+let ENGINE: Engine | undefined = undefined;
export const VizPage = createPage({
oncreate() {
diff --git a/ui/src/open_perfetto_trace/index.html b/ui/src/open_perfetto_trace/index.html
new file mode 100644
index 0000000..1fb52e3
--- /dev/null
+++ b/ui/src/open_perfetto_trace/index.html
@@ -0,0 +1,59 @@
+<!doctype html>
+<html lang='en-us'>
+<head>
+ <script src="open_perfetto_trace_bundle.js"></script>
+ <style type="text/css">
+ html { font-family: Roboto, sans-serif; }
+ main {display: flex; flex-direction: column; max-width: 800px;}
+ main > * { margin: 5px; }
+ </style>
+</head>
+<body>
+
+
+<main>
+ <select id='trace_source' size='5'>
+ <option>https://storage.googleapis.com/perfetto-misc/example_android_trace_15s</option>
+ <option selected>https://storage.googleapis.com/perfetto-misc/chrome_example_wikipedia.perfetto_trace.gz</option>
+ </select>
+ <label>Or select a local file: <input type="file" id="file"></label>
+ <input type='button' value='Fetch and open selected trace' id='fetch'>
+ <label><input type='checkbox' id='show_progress' checked="checked">Show progress dialog</label>
+ <label><input type='checkbox' id='new_tab'>Open in new tab</label>
+ <label><input type='checkbox' id='hide_sidebar'>Hide sidebar in Perfetto UI</label>
+
+</main>
+
+<script type='text/javascript'>
+
+function getCheckbox(id) {
+ return document.getElementById(id).checked;
+}
+
+document.getElementById('fetch').addEventListener('click', () => {
+ const opts = {};
+
+ if (location.host.startsWith('127.0.0.1') ||
+ location.host.startsWith('localhost')) {
+ opts.uiUrl = `${location.protocol}//${location.host}`;
+ }
+
+ opts.statusDialog = getCheckbox('show_progress');
+ opts.newTab = getCheckbox('new_tab');
+ opts.hideSidebar = getCheckbox('hide_sidebar');
+
+ const fileInput = document.getElementById('file');
+ let traceSource;
+ if (fileInput.files.length > 0) {
+ traceSource = fileInput.files[0]
+ } else {
+ traceSource = document.getElementById('trace_source').value;
+ }
+
+ open_perfetto_trace(traceSource, opts);
+});
+
+</script>
+</body>
+</html>
+
diff --git a/ui/src/open_perfetto_trace/index.ts b/ui/src/open_perfetto_trace/index.ts
new file mode 100644
index 0000000..5462783
--- /dev/null
+++ b/ui/src/open_perfetto_trace/index.ts
@@ -0,0 +1,191 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// open-perfetto-trace is a standalone JS/TS library that can be used in other
+// projects to facilitate the deep linking into perfetto. It allows opening
+// trace files or URL with ui.perfetto.dev, handling all the handshake with it.
+
+const PERFETTO_UI_URL = 'https://ui.perfetto.dev';
+
+interface OpenTraceOptions {
+ // If true (default) shows a popup dialog with a progress bar that informs
+ // about the status of the fetch. This is only relevant when the trace source
+ // is a url.
+ statusDialog?: boolean;
+
+ // Opens the trace in a new tab.
+ newTab?: boolean;
+
+ // Override the referrer. Useful for scripts such as
+ // record_android_trace to record where the trace is coming from.
+ referrer?: string;
+
+ // For the 'mode' of the UI. For example when the mode is 'embedded'
+ // some features are disabled.
+ mode: 'embedded' | undefined;
+
+ // Hides the sidebar in the opened perfetto UI.
+ hideSidebar?: boolean;
+
+ ts?: string;
+
+ dur?: string;
+ tid?: string;
+ pid?: string;
+ query?: string;
+ visStart?: string;
+ visEnd?: string;
+
+ // Used to override ui.perfetto.dev with a custom hosted URL.
+ // Useful for testing.
+ uiUrl?: string;
+}
+
+// Opens a trace in the Perfetto UI.
+// `source` can be either:
+// - A blob (e.g. a File).
+// - A URL.
+export default function openPerfettoTrace(
+ source: Blob | string,
+ opts?: OpenTraceOptions,
+) {
+ if (source instanceof Blob) {
+ return openTraceBlob(source, opts);
+ } else if (typeof source === 'string') {
+ return fetchAndOpenTrace(source, opts);
+ }
+}
+
+function openTraceBlob(blob: Blob, opts?: OpenTraceOptions) {
+ const form = document.createElement('form');
+ form.method = 'POST';
+ form.style.visibility = 'hidden';
+ form.enctype = 'multipart/form-data';
+ const uiUrl = opts?.uiUrl ?? PERFETTO_UI_URL;
+ form.action = `${uiUrl}/_open_trace/${Date.now()}`;
+ if (opts?.newTab === true) {
+ form.target = '_blank';
+ }
+ const fileInput = document.createElement('input');
+ fileInput.name = 'trace';
+ fileInput.type = 'file';
+ const dataTransfer = new DataTransfer();
+ dataTransfer.items.add(new File([blob], 'trace.file'));
+ fileInput.files = dataTransfer.files;
+ form.appendChild(fileInput);
+ for (const [key, value] of Object.entries(opts ?? {})) {
+ const varInput = document.createElement('input');
+ varInput.type = 'hidden';
+ varInput.name = key;
+ varInput.value = value;
+ form.appendChild(varInput);
+ }
+ document.body.appendChild(form);
+ form.submit();
+}
+
+function fetchAndOpenTrace(url: string, opts?: OpenTraceOptions) {
+ updateProgressDiv({status: 'Fetching trace'}, opts);
+ const xhr = new XMLHttpRequest();
+ xhr.addEventListener('progress', (event) => {
+ if (event.lengthComputable) {
+ updateProgressDiv(
+ {
+ status: `Fetching trace (${Math.round(event.loaded / 1000)} KB)`,
+ progress: event.loaded / event.total,
+ },
+ opts,
+ );
+ }
+ });
+ xhr.addEventListener('loadend', () => {
+ if (xhr.readyState === 4 && xhr.status === 200) {
+ const blob = xhr.response as Blob;
+ updateProgressDiv({status: 'Opening trace'}, opts);
+ openTraceBlob(blob, opts);
+ updateProgressDiv({close: true}, opts);
+ }
+ });
+ xhr.addEventListener('error', () => {
+ updateProgressDiv({status: 'Failed to fetch trace'}, opts);
+ });
+ xhr.responseType = 'blob';
+ xhr.overrideMimeType('application/octet-stream');
+ xhr.open('GET', url);
+ xhr.send();
+}
+
+interface ProgressDivOpts {
+ status?: string;
+ progress?: number;
+ close?: boolean;
+}
+
+function updateProgressDiv(progress: ProgressDivOpts, opts?: OpenTraceOptions) {
+ if (opts?.statusDialog === false) return;
+
+ const kDivId = 'open_perfetto_trace';
+ let div = document.getElementById(kDivId);
+ if (!div) {
+ div = document.createElement('div');
+ div.id = kDivId;
+ div.style.all = 'initial';
+ div.style.position = 'fixed';
+ div.style.bottom = '10px';
+ div.style.left = '0';
+ div.style.right = '0';
+ div.style.width = 'fit-content';
+ div.style.height = '20px';
+ div.style.padding = '10px';
+ div.style.zIndex = '99';
+ div.style.margin = 'auto';
+ div.style.backgroundColor = '#fff';
+ div.style.color = '#333';
+ div.style.fontFamily = 'monospace';
+ div.style.fontSize = '12px';
+ div.style.border = '1px solid #eee';
+ div.style.boxShadow = '0 0 20px #aaa';
+ div.style.display = 'flex';
+ div.style.flexDirection = 'column';
+
+ const title = document.createElement('div');
+ title.className = 'perfetto-open-title';
+ title.innerText = 'Opening perfetto trace';
+ title.style.fontWeight = '12px';
+ title.style.textAlign = 'center';
+ div.appendChild(title);
+
+ const progressbar = document.createElement('progress');
+ progressbar.className = 'perfetto-open-progress';
+ progressbar.style.width = '200px';
+ progressbar.value = 0;
+ div.appendChild(progressbar);
+
+ document.body.appendChild(div);
+ }
+ const title = div.querySelector('.perfetto-open-title') as HTMLElement;
+ if (progress.status !== undefined) {
+ title.innerText = progress.status;
+ }
+ const bar = div.querySelector('.perfetto-open-progress') as HTMLInputElement;
+ if (progress.progress === undefined) {
+ bar.style.visibility = 'hidden';
+ } else {
+ bar.style.visibility = 'visible';
+ bar.value = `${progress.progress}`;
+ }
+ if (progress.close === true) {
+ div.remove();
+ }
+}
diff --git a/ui/src/open_perfetto_trace/tsconfig.json b/ui/src/open_perfetto_trace/tsconfig.json
new file mode 100644
index 0000000..66bf290
--- /dev/null
+++ b/ui/src/open_perfetto_trace/tsconfig.json
@@ -0,0 +1,16 @@
+{
+ "extends": "../../tsconfig.base.json",
+ "include": [ "." ],
+ "exclude": [
+ "../gen/"
+ ],
+ "compilerOptions": {
+ "outDir": "../../out/tsc/open_perfetto_trace",
+ "lib": [
+ "dom", // Need to be explicitly mentioned now since we're overriding default included libs.
+ "es2021", // Need this to use Promise.allSettled, replaceAll, etc
+ ],
+ "esModuleInterop": true,
+ "allowSyntheticDefaultImports": true,
+ }
+}
diff --git a/ui/src/plugins/dev.perfetto.AndroidLongBatteryTracing/index.ts b/ui/src/plugins/dev.perfetto.AndroidLongBatteryTracing/index.ts
index 2a22aff..8f360b4 100644
--- a/ui/src/plugins/dev.perfetto.AndroidLongBatteryTracing/index.ts
+++ b/ui/src/plugins/dev.perfetto.AndroidLongBatteryTracing/index.ts
@@ -13,7 +13,7 @@
// limitations under the License.
import {Plugin, PluginContextTrace, PluginDescriptor} from '../../public';
-import {EngineProxy} from '../../trace_processor/engine';
+import {Engine} from '../../trace_processor/engine';
import {
SimpleSliceTrack,
SimpleSliceTrackConfig,
@@ -1694,7 +1694,7 @@
);
}
- async findFeatures(e: EngineProxy): Promise<Set<string>> {
+ async findFeatures(e: Engine): Promise<Set<string>> {
const features = new Set<string>();
const addFeatures = async (q: string) => {
diff --git a/ui/src/plugins/dev.perfetto.AndroidNetwork/index.ts b/ui/src/plugins/dev.perfetto.AndroidNetwork/index.ts
index 9cb8405..34fd087 100644
--- a/ui/src/plugins/dev.perfetto.AndroidNetwork/index.ts
+++ b/ui/src/plugins/dev.perfetto.AndroidNetwork/index.ts
@@ -14,14 +14,14 @@
import {Plugin, PluginContextTrace, PluginDescriptor} from '../../public';
import {addDebugSliceTrack} from '../../public';
-import {EngineProxy} from '../../trace_processor/engine';
+import {Engine} from '../../trace_processor/engine';
class AndroidNetwork implements Plugin {
// Adds a debug track using the provided query and given columns. The columns
// must be start with ts, dur, and a name column. The name column and all
// following columns are shown as arguments in slice details.
async addSimpleTrack(
- engine: EngineProxy,
+ engine: Engine,
trackName: string,
tableOrQuery: string,
columns: string[],
diff --git a/ui/src/plugins/dev.perfetto.AndroidPerf/index.ts b/ui/src/plugins/dev.perfetto.AndroidPerf/index.ts
index a53ff20..25f11f4 100644
--- a/ui/src/plugins/dev.perfetto.AndroidPerf/index.ts
+++ b/ui/src/plugins/dev.perfetto.AndroidPerf/index.ts
@@ -18,11 +18,11 @@
PluginContextTrace,
PluginDescriptor,
} from '../../public';
-import {EngineProxy} from '../../trace_processor/engine';
+import {Engine} from '../../trace_processor/engine';
class AndroidPerf implements Plugin {
async addAppProcessStartsDebugTrack(
- engine: EngineProxy,
+ engine: Engine,
reason: string,
sliceName: string,
): Promise<void> {
diff --git a/ui/src/public/index.ts b/ui/src/public/index.ts
index f3e4338..7fb2e44 100644
--- a/ui/src/public/index.ts
+++ b/ui/src/public/index.ts
@@ -20,10 +20,10 @@
import {ColorScheme} from '../core/colorizer';
import {LegacySelection} from '../common/state';
import {PanelSize} from '../frontend/panel';
-import {EngineProxy} from '../trace_processor/engine';
+import {Engine} from '../trace_processor/engine';
import {UntypedEventSet} from '../core/event_set';
-export {EngineProxy} from '../trace_processor/engine';
+export {Engine} from '../trace_processor/engine';
export {
LONG,
LONG_NULL,
@@ -344,7 +344,7 @@
// currently loaded trace. Passed to trace-relevant hooks on a plugin instead of
// PluginContext.
export interface PluginContextTrace extends PluginContext {
- readonly engine: EngineProxy;
+ readonly engine: Engine;
// Control over the main timeline.
timeline: {
diff --git a/ui/src/service_worker/service_worker.ts b/ui/src/service_worker/service_worker.ts
index dc467f0..699db43 100644
--- a/ui/src/service_worker/service_worker.ts
+++ b/ui/src/service_worker/service_worker.ts
@@ -45,6 +45,7 @@
const LOG_TAG = `ServiceWorker: `;
const CACHE_NAME = 'ui-perfetto-dev';
+const OPEN_TRACE_PREFIX = '/_open_trace'
// If the fetch() for the / doesn't respond within 3s, return a cached version.
// This is to avoid that a user waits too much if on a flaky network.
@@ -54,6 +55,9 @@
// in the background.
const INSTALL_TIMEOUT_MS = 30000;
+// Files passed to POST /_open_trace/NNNN.
+let postedFiles = new Map<string, File>();
+
// The install() event is fired:
// 1. On the first visit, when there is no SW installed.
// 2. Every time the user opens the site and the version has been updated (they
@@ -128,6 +132,7 @@
});
self.addEventListener('fetch', (event) => {
+
// The early return here will cause the browser to fall back on standard
// network-based fetch.
if (!shouldHandleHttpRequest(event.request)) {
@@ -149,6 +154,8 @@
const url = new URL(req.url);
if (url.pathname === '/live_reload') return false;
+ if (url.pathname.startsWith(OPEN_TRACE_PREFIX)) return true;
+
return req.method === 'GET' && url.origin === self.location.origin;
}
@@ -184,6 +191,8 @@
// network fetch.
const cachedRes = await caches.match(new Request('/'), cacheOps);
if (cachedRes) return cachedRes;
+ } else if (url.pathname.startsWith(OPEN_TRACE_PREFIX)) {
+ return await handleOpenTraceRequest(req);
}
const cachedRes = await caches.match(req, cacheOps);
@@ -198,6 +207,60 @@
return fetch(req);
}
+// Handles GET and POST requests to /_open_trace/NNNN, where NNNN is typically a
+// random token generated by the client.
+// This works as follows:
+// - The client does a POST request to /_open_trace/NNNN passing the trace blob
+// as multipart-data, alongside other options like hideSidebar & co that we
+// support in the usual querystring (see router.ts)
+// - The SW takes the file and puts it in the global variable `postedFiles`.
+// - The SW responds to the POST request with a redirect to
+// ui.perfetto.dev/#!/?url=https://ui.perfetto.dev/_open_trace/NNNN&other_args
+// - When the new ui.perfetto.dev is reloaded, it will naturally try to fetch
+// the trace from /_open_trace/NNNN, this time via a GET request.
+// - The SW intercepts the GET request and returns the file previosly stored in
+// `postedFiles`.
+// We use postedFiles here to handle the case of progammatically POST-ing to >1
+// instances of ui.perfetto.dev simultaneously, to avoid races.
+// Note that we should not use a global variable for `postedFiles` but we should
+// use the CacheAPI because, technically speaking, the SW could be disposed
+// and respawned in between the POST and the GET request. In practice, however,
+// SWs are disposed only after 30s seconds of idleness. The POST->GET requests
+// happen back-to-back..
+async function handleOpenTraceRequest(req: Request): Promise<Response> {
+ const url = new URL(req.url);
+ console.assert(url.pathname.startsWith(OPEN_TRACE_PREFIX));
+ const fileKey = url.pathname.substring(OPEN_TRACE_PREFIX.length);
+ if (req.method === 'POST') {
+ const formData = await req.formData();
+ const qsParams = new URLSearchParams();
+ // Iterate over the POST fields and copy them over the querystring in
+ // the hash, with the exception of the trace file. The trace file is
+ // kept in the serviceworker and passed as a url= argument.
+ formData.forEach((value, key) => {
+ if (key === 'trace') {
+ if (value instanceof File) {
+ postedFiles.set(fileKey, value);
+ qsParams.set('url', req.url);
+ }
+ return;
+ }
+ qsParams.set(key, `${value}`);
+ }); // formData.forEach()
+ return Response.redirect(`${url.protocol}//${url.host}/#!/?${qsParams}`);
+ }
+
+ // else... method == 'GET'
+ const file = postedFiles.get(fileKey);
+ if (file !== undefined) {
+ postedFiles.delete(fileKey);
+ return new Response(file);
+ }
+
+ // The file /_open_trace/NNNN does not exist.
+ return Response.error();
+}
+
async function installAppVersionIntoCache(version: string) {
const manifestUrl = `${version}/manifest.json`;
try {
diff --git a/ui/src/trace_processor/engine.ts b/ui/src/trace_processor/engine.ts
index b234c16..90901c5 100644
--- a/ui/src/trace_processor/engine.ts
+++ b/ui/src/trace_processor/engine.ts
@@ -13,7 +13,6 @@
// limitations under the License.
import {defer, Deferred} from '../base/deferred';
-import {Disposable} from '../base/disposable';
import {assertExists, assertTrue} from '../base/logging';
import {duration, Span, Time, time, TimeSpan} from '../base/time';
import {
@@ -42,6 +41,7 @@
} from './query_result';
import TPM = TraceProcessorRpc.TraceProcessorMethod;
+import {Disposable} from '../base/disposable';
export interface LoadingTracker {
beginLoading(): void;
@@ -66,6 +66,19 @@
ftraceDropUntilAllCpusValid: boolean;
}
+export interface Engine {
+ execute(sqlQuery: string, tag?: string): Promise<QueryResult> & QueryResult;
+ query(sqlQuery: string, tag?: string): Promise<QueryResult>;
+ getCpus(): Promise<number[]>;
+ getNumberOfGpus(): Promise<number>;
+ getTracingMetadataTimeBounds(): Promise<Span<time, duration>>;
+ computeMetric(
+ metrics: string[],
+ format: 'json' | 'prototext' | 'proto',
+ ): Promise<string | Uint8Array>;
+ readonly isAlive: boolean;
+}
+
// Abstract interface of a trace proccessor.
// This is the TypeScript equivalent of src/trace_processor/rpc.h.
// There are two concrete implementations:
@@ -77,7 +90,7 @@
// 1. Implement the abstract rpcSendRequestBytes() function, sending the
// proto-encoded TraceProcessorRpc requests to the TraceProcessor instance.
// 2. Call onRpcResponseBytes() when response data is received.
-export abstract class Engine {
+export abstract class EngineBase implements Engine {
abstract readonly id: string;
private _cpus?: number[];
private _numGpus?: number;
@@ -93,6 +106,7 @@
private pendingComputeMetrics = new Array<Deferred<string | Uint8Array>>();
private pendingReadMetatrace?: Deferred<DisableAndReadMetatraceResult>;
private _isMetatracingEnabled = false;
+ readonly isAlive = false;
constructor(tracker?: LoadingTracker) {
this.loadingTracker = tracker ? tracker : new NullLoadingTracker();
@@ -502,10 +516,9 @@
}
}
-// Lightweight wrapper over Engine exposing only `query` method and annotating
-// all queries going through it with a tag.
-export class EngineProxy implements Disposable {
- private engine: Engine;
+// Lightweight engine proxy which annotates all queries with a tag
+export class EngineProxy implements Engine, Disposable {
+ private engine: EngineBase;
private tag: string;
private _isAlive: boolean;
@@ -513,7 +526,7 @@
return this._isAlive;
}
- constructor(engine: Engine, tag: string) {
+ constructor(engine: EngineBase, tag: string) {
this.engine = engine;
this.tag = tag;
this._isAlive = true;
@@ -557,6 +570,10 @@
return this.engine.getNumberOfGpus();
}
+ async getTracingMetadataTimeBounds(): Promise<Span<time, bigint>> {
+ return this.engine.getTracingMetadataTimeBounds();
+ }
+
get engineId(): string {
return this.engine.id;
}
diff --git a/ui/src/trace_processor/http_rpc_engine.ts b/ui/src/trace_processor/http_rpc_engine.ts
index dfd9bc8..720e2b6 100644
--- a/ui/src/trace_processor/http_rpc_engine.ts
+++ b/ui/src/trace_processor/http_rpc_engine.ts
@@ -15,7 +15,7 @@
import {fetchWithTimeout} from '../base/http_utils';
import {assertExists} from '../base/logging';
import {StatusResult} from '../protos';
-import {Engine, LoadingTracker} from '../trace_processor/engine';
+import {EngineBase, LoadingTracker} from '../trace_processor/engine';
const RPC_CONNECT_TIMEOUT_MS = 2000;
@@ -25,7 +25,7 @@
failure?: string;
}
-export class HttpRpcEngine extends Engine {
+export class HttpRpcEngine extends EngineBase {
readonly id: string;
errorHandler: (err: string) => void = () => {};
private requestQueue = new Array<Uint8Array>();
diff --git a/ui/src/trace_processor/wasm_engine_proxy.ts b/ui/src/trace_processor/wasm_engine_proxy.ts
index 42740e2..163fac5 100644
--- a/ui/src/trace_processor/wasm_engine_proxy.ts
+++ b/ui/src/trace_processor/wasm_engine_proxy.ts
@@ -13,7 +13,7 @@
// limitations under the License.
import {assertExists, assertTrue} from '../base/logging';
-import {Engine, LoadingTracker} from '../trace_processor/engine';
+import {EngineBase, LoadingTracker} from '../trace_processor/engine';
let bundlePath: string;
let idleWasmWorker: Worker;
@@ -47,7 +47,7 @@
* This implementation of Engine uses a WASM backend hosted in a separate
* worker thread.
*/
-export class WasmEngineProxy extends Engine {
+export class WasmEngineProxy extends EngineBase {
readonly id: string;
private port: MessagePort;
diff --git a/ui/src/widgets/vega_view.ts b/ui/src/widgets/vega_view.ts
index 97d9826..39be606 100644
--- a/ui/src/widgets/vega_view.ts
+++ b/ui/src/widgets/vega_view.ts
@@ -20,7 +20,7 @@
import {getErrorMessage} from '../base/errors';
import {isString, shallowEquals} from '../base/object_utils';
import {SimpleResizeObserver} from '../base/resize_observer';
-import {EngineProxy} from '../trace_processor/engine';
+import {Engine} from '../trace_processor/engine';
import {QueryError} from '../trace_processor/query_result';
import {scheduleFullRedraw} from '../widgets/raf';
import {Spinner} from '../widgets/spinner';
@@ -45,7 +45,7 @@
interface VegaViewAttrs {
spec: string;
data: VegaViewData;
- engine?: EngineProxy;
+ engine?: Engine;
}
// VegaWrapper is in exactly one of these states:
@@ -62,10 +62,10 @@
}
class EngineLoader implements vega.Loader {
- private engine?: EngineProxy;
+ private engine?: Engine;
private loader: vega.Loader;
- constructor(engine: EngineProxy | undefined) {
+ constructor(engine: Engine | undefined) {
this.engine = engine;
this.loader = vega.loader();
}
@@ -125,7 +125,7 @@
private pending?: Promise<vega.View>;
private _status: Status;
private _error?: string;
- private _engine?: EngineProxy;
+ private _engine?: Engine;
constructor(dom: Element) {
this.dom = dom;
@@ -155,7 +155,7 @@
this.updateView();
}
- set engine(engine: EngineProxy | undefined) {
+ set engine(engine: Engine | undefined) {
this._engine = engine;
}