Merge "ui: make ftrace track 10x faster" into main
diff --git a/Android.bp b/Android.bp
index 69c599d..a0b685c 100644
--- a/Android.bp
+++ b/Android.bp
@@ -5481,6 +5481,7 @@
"protos/perfetto/metrics/chrome/dropped_frames.proto",
"protos/perfetto/metrics/chrome/frame_times.proto",
"protos/perfetto/metrics/chrome/histogram_hashes.proto",
+ "protos/perfetto/metrics/chrome/histogram_summaries.proto",
"protos/perfetto/metrics/chrome/long_latency.proto",
"protos/perfetto/metrics/chrome/media_metric.proto",
"protos/perfetto/metrics/chrome/performance_mark_hashes.proto",
@@ -6790,6 +6791,7 @@
"protos/perfetto/trace/ftrace/fastrpc.proto",
"protos/perfetto/trace/ftrace/fence.proto",
"protos/perfetto/trace/ftrace/filemap.proto",
+ "protos/perfetto/trace/ftrace/fs.proto",
"protos/perfetto/trace/ftrace/ftrace.proto",
"protos/perfetto/trace/ftrace/ftrace_event.proto",
"protos/perfetto/trace/ftrace/ftrace_event_bundle.proto",
@@ -7220,6 +7222,7 @@
"protos/perfetto/trace/ftrace/fastrpc.proto",
"protos/perfetto/trace/ftrace/fence.proto",
"protos/perfetto/trace/ftrace/filemap.proto",
+ "protos/perfetto/trace/ftrace/fs.proto",
"protos/perfetto/trace/ftrace/ftrace.proto",
"protos/perfetto/trace/ftrace/ftrace_event.proto",
"protos/perfetto/trace/ftrace/ftrace_event_bundle.proto",
@@ -7312,6 +7315,7 @@
"external/perfetto/protos/perfetto/trace/ftrace/fastrpc.gen.cc",
"external/perfetto/protos/perfetto/trace/ftrace/fence.gen.cc",
"external/perfetto/protos/perfetto/trace/ftrace/filemap.gen.cc",
+ "external/perfetto/protos/perfetto/trace/ftrace/fs.gen.cc",
"external/perfetto/protos/perfetto/trace/ftrace/ftrace.gen.cc",
"external/perfetto/protos/perfetto/trace/ftrace/ftrace_event.gen.cc",
"external/perfetto/protos/perfetto/trace/ftrace/ftrace_event_bundle.gen.cc",
@@ -7404,6 +7408,7 @@
"external/perfetto/protos/perfetto/trace/ftrace/fastrpc.gen.h",
"external/perfetto/protos/perfetto/trace/ftrace/fence.gen.h",
"external/perfetto/protos/perfetto/trace/ftrace/filemap.gen.h",
+ "external/perfetto/protos/perfetto/trace/ftrace/fs.gen.h",
"external/perfetto/protos/perfetto/trace/ftrace/ftrace.gen.h",
"external/perfetto/protos/perfetto/trace/ftrace/ftrace_event.gen.h",
"external/perfetto/protos/perfetto/trace/ftrace/ftrace_event_bundle.gen.h",
@@ -7492,6 +7497,7 @@
"protos/perfetto/trace/ftrace/fastrpc.proto",
"protos/perfetto/trace/ftrace/fence.proto",
"protos/perfetto/trace/ftrace/filemap.proto",
+ "protos/perfetto/trace/ftrace/fs.proto",
"protos/perfetto/trace/ftrace/ftrace.proto",
"protos/perfetto/trace/ftrace/ftrace_event.proto",
"protos/perfetto/trace/ftrace/ftrace_event_bundle.proto",
@@ -7583,6 +7589,7 @@
"external/perfetto/protos/perfetto/trace/ftrace/fastrpc.pb.cc",
"external/perfetto/protos/perfetto/trace/ftrace/fence.pb.cc",
"external/perfetto/protos/perfetto/trace/ftrace/filemap.pb.cc",
+ "external/perfetto/protos/perfetto/trace/ftrace/fs.pb.cc",
"external/perfetto/protos/perfetto/trace/ftrace/ftrace.pb.cc",
"external/perfetto/protos/perfetto/trace/ftrace/ftrace_event.pb.cc",
"external/perfetto/protos/perfetto/trace/ftrace/ftrace_event_bundle.pb.cc",
@@ -7674,6 +7681,7 @@
"external/perfetto/protos/perfetto/trace/ftrace/fastrpc.pb.h",
"external/perfetto/protos/perfetto/trace/ftrace/fence.pb.h",
"external/perfetto/protos/perfetto/trace/ftrace/filemap.pb.h",
+ "external/perfetto/protos/perfetto/trace/ftrace/fs.pb.h",
"external/perfetto/protos/perfetto/trace/ftrace/ftrace.pb.h",
"external/perfetto/protos/perfetto/trace/ftrace/ftrace_event.pb.h",
"external/perfetto/protos/perfetto/trace/ftrace/ftrace_event_bundle.pb.h",
@@ -7762,6 +7770,7 @@
"protos/perfetto/trace/ftrace/fastrpc.proto",
"protos/perfetto/trace/ftrace/fence.proto",
"protos/perfetto/trace/ftrace/filemap.proto",
+ "protos/perfetto/trace/ftrace/fs.proto",
"protos/perfetto/trace/ftrace/ftrace.proto",
"protos/perfetto/trace/ftrace/ftrace_event.proto",
"protos/perfetto/trace/ftrace/ftrace_event_bundle.proto",
@@ -7854,6 +7863,7 @@
"external/perfetto/protos/perfetto/trace/ftrace/fastrpc.pbzero.cc",
"external/perfetto/protos/perfetto/trace/ftrace/fence.pbzero.cc",
"external/perfetto/protos/perfetto/trace/ftrace/filemap.pbzero.cc",
+ "external/perfetto/protos/perfetto/trace/ftrace/fs.pbzero.cc",
"external/perfetto/protos/perfetto/trace/ftrace/ftrace.pbzero.cc",
"external/perfetto/protos/perfetto/trace/ftrace/ftrace_event.pbzero.cc",
"external/perfetto/protos/perfetto/trace/ftrace/ftrace_event_bundle.pbzero.cc",
@@ -7946,6 +7956,7 @@
"external/perfetto/protos/perfetto/trace/ftrace/fastrpc.pbzero.h",
"external/perfetto/protos/perfetto/trace/ftrace/fence.pbzero.h",
"external/perfetto/protos/perfetto/trace/ftrace/filemap.pbzero.h",
+ "external/perfetto/protos/perfetto/trace/ftrace/fs.pbzero.h",
"external/perfetto/protos/perfetto/trace/ftrace/ftrace.pbzero.h",
"external/perfetto/protos/perfetto/trace/ftrace/ftrace_event.pbzero.h",
"external/perfetto/protos/perfetto/trace/ftrace/ftrace_event_bundle.pbzero.h",
@@ -13224,6 +13235,7 @@
"src/trace_processor/metrics/sql/chrome/chrome_args_class_names.sql",
"src/trace_processor/metrics/sql/chrome/chrome_event_metadata.sql",
"src/trace_processor/metrics/sql/chrome/chrome_histogram_hashes.sql",
+ "src/trace_processor/metrics/sql/chrome/chrome_histogram_summaries.sql",
"src/trace_processor/metrics/sql/chrome/chrome_input_to_browser_intervals.sql",
"src/trace_processor/metrics/sql/chrome/chrome_input_to_browser_intervals_base.sql",
"src/trace_processor/metrics/sql/chrome/chrome_input_to_browser_intervals_template.sql",
@@ -15152,6 +15164,7 @@
"protos/perfetto/trace/ftrace/fastrpc.proto",
"protos/perfetto/trace/ftrace/fence.proto",
"protos/perfetto/trace/ftrace/filemap.proto",
+ "protos/perfetto/trace/ftrace/fs.proto",
"protos/perfetto/trace/ftrace/ftrace.proto",
"protos/perfetto/trace/ftrace/ftrace_event.proto",
"protos/perfetto/trace/ftrace/ftrace_event_bundle.proto",
@@ -16493,6 +16506,7 @@
"protos/perfetto/trace/ftrace/fastrpc.proto",
"protos/perfetto/trace/ftrace/fence.proto",
"protos/perfetto/trace/ftrace/filemap.proto",
+ "protos/perfetto/trace/ftrace/fs.proto",
"protos/perfetto/trace/ftrace/ftrace.proto",
"protos/perfetto/trace/ftrace/ftrace_event.proto",
"protos/perfetto/trace/ftrace/ftrace_event_bundle.proto",
diff --git a/BUILD b/BUILD
index d4f1f1b..d4d3aff 100644
--- a/BUILD
+++ b/BUILD
@@ -2541,6 +2541,7 @@
"src/trace_processor/metrics/sql/chrome/chrome_args_class_names.sql",
"src/trace_processor/metrics/sql/chrome/chrome_event_metadata.sql",
"src/trace_processor/metrics/sql/chrome/chrome_histogram_hashes.sql",
+ "src/trace_processor/metrics/sql/chrome/chrome_histogram_summaries.sql",
"src/trace_processor/metrics/sql/chrome/chrome_input_to_browser_intervals.sql",
"src/trace_processor/metrics/sql/chrome/chrome_input_to_browser_intervals_base.sql",
"src/trace_processor/metrics/sql/chrome/chrome_input_to_browser_intervals_template.sql",
@@ -5230,6 +5231,7 @@
"protos/perfetto/metrics/chrome/dropped_frames.proto",
"protos/perfetto/metrics/chrome/frame_times.proto",
"protos/perfetto/metrics/chrome/histogram_hashes.proto",
+ "protos/perfetto/metrics/chrome/histogram_summaries.proto",
"protos/perfetto/metrics/chrome/long_latency.proto",
"protos/perfetto/metrics/chrome/media_metric.proto",
"protos/perfetto/metrics/chrome/performance_mark_hashes.proto",
@@ -5625,6 +5627,7 @@
"protos/perfetto/trace/ftrace/fastrpc.proto",
"protos/perfetto/trace/ftrace/fence.proto",
"protos/perfetto/trace/ftrace/filemap.proto",
+ "protos/perfetto/trace/ftrace/fs.proto",
"protos/perfetto/trace/ftrace/ftrace.proto",
"protos/perfetto/trace/ftrace/ftrace_event.proto",
"protos/perfetto/trace/ftrace/ftrace_event_bundle.proto",
diff --git a/protos/perfetto/metrics/chrome/BUILD.gn b/protos/perfetto/metrics/chrome/BUILD.gn
index bf47885..c0b0c36 100644
--- a/protos/perfetto/metrics/chrome/BUILD.gn
+++ b/protos/perfetto/metrics/chrome/BUILD.gn
@@ -27,6 +27,7 @@
"dropped_frames.proto",
"frame_times.proto",
"histogram_hashes.proto",
+ "histogram_summaries.proto",
"long_latency.proto",
"media_metric.proto",
"performance_mark_hashes.proto",
diff --git a/protos/perfetto/metrics/chrome/all_chrome_metrics.proto b/protos/perfetto/metrics/chrome/all_chrome_metrics.proto
index 596bc7f..ebc0ac5 100644
--- a/protos/perfetto/metrics/chrome/all_chrome_metrics.proto
+++ b/protos/perfetto/metrics/chrome/all_chrome_metrics.proto
@@ -23,6 +23,7 @@
import "protos/perfetto/metrics/chrome/dropped_frames.proto";
import "protos/perfetto/metrics/chrome/frame_times.proto";
import "protos/perfetto/metrics/chrome/histogram_hashes.proto";
+import "protos/perfetto/metrics/chrome/histogram_summaries.proto";
import "protos/perfetto/metrics/chrome/long_latency.proto";
import "protos/perfetto/metrics/chrome/media_metric.proto";
import "protos/perfetto/metrics/chrome/performance_mark_hashes.proto";
@@ -53,4 +54,5 @@
optional ChromeUnsymbolizedArgs chrome_unsymbolized_args = 1014;
optional ChromeArgsClassNames chrome_args_class_names = 1015;
optional ChromeScrollJankV3 chrome_scroll_jank_v3 = 1017;
+ optional ChromeHistogramSummaries chrome_histogram_summaries = 1018;
}
diff --git a/protos/perfetto/metrics/chrome/histogram_summaries.proto b/protos/perfetto/metrics/chrome/histogram_summaries.proto
new file mode 100644
index 0000000..57dad94
--- /dev/null
+++ b/protos/perfetto/metrics/chrome/histogram_summaries.proto
@@ -0,0 +1,43 @@
+
+/*
+ * 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.
+ */
+
+syntax = "proto2";
+
+package perfetto.protos;
+
+message HistogramSummary {
+ // The name of the histogram event
+ optional string name = 1;
+ // The avarage value of the histogram event
+ optional int64 mean = 2;
+ // The number of the histogram event in the trace track
+ optional uint32 count = 3;
+ // The sum of value of the histogram event
+ optional int64 sum = 4;
+ // The maximum value of the histogram event
+ optional int64 max = 5;
+ // The 90 percentile value of the histogram event
+ optional int64 p90 = 6;
+ // The 50 percentile (median) value of the histogram event
+ optional int64 p50 = 7;
+}
+
+// The list of the summary of Chrome Histograms in trace track events.
+// This includes the statistic information of each histograms from Chrome.
+message ChromeHistogramSummaries {
+ repeated HistogramSummary histogram_summary = 1;
+}
diff --git a/protos/perfetto/trace/ftrace/all_protos.gni b/protos/perfetto/trace/ftrace/all_protos.gni
index 7319dd4..8565125 100644
--- a/protos/perfetto/trace/ftrace/all_protos.gni
+++ b/protos/perfetto/trace/ftrace/all_protos.gni
@@ -42,6 +42,7 @@
"fastrpc.proto",
"fence.proto",
"filemap.proto",
+ "fs.proto",
"ftrace.proto",
"g2d.proto",
"google_icc_trace.proto",
diff --git a/protos/perfetto/trace/ftrace/fs.proto b/protos/perfetto/trace/ftrace/fs.proto
new file mode 100644
index 0000000..4cc3531
--- /dev/null
+++ b/protos/perfetto/trace/ftrace/fs.proto
@@ -0,0 +1,15 @@
+// Autogenerated by:
+// ../../src/tools/ftrace_proto_gen/ftrace_proto_gen.cc
+// Do not edit.
+
+syntax = "proto2";
+package perfetto.protos;
+
+message DoSysOpenFtraceEvent {
+ optional string filename = 1;
+ optional int32 flags = 2;
+ optional int32 mode = 3;
+}
+message OpenExecFtraceEvent {
+ optional string filename = 1;
+}
diff --git a/protos/perfetto/trace/ftrace/ftrace_event.proto b/protos/perfetto/trace/ftrace/ftrace_event.proto
index b363eb0..40e7113 100644
--- a/protos/perfetto/trace/ftrace/ftrace_event.proto
+++ b/protos/perfetto/trace/ftrace/ftrace_event.proto
@@ -42,6 +42,7 @@
import "protos/perfetto/trace/ftrace/fastrpc.proto";
import "protos/perfetto/trace/ftrace/fence.proto";
import "protos/perfetto/trace/ftrace/filemap.proto";
+import "protos/perfetto/trace/ftrace/fs.proto";
import "protos/perfetto/trace/ftrace/ftrace.proto";
import "protos/perfetto/trace/ftrace/g2d.proto";
import "protos/perfetto/trace/ftrace/google_icc_trace.proto";
@@ -683,5 +684,7 @@
DevfreqFrequencyFtraceEvent devfreq_frequency = 541;
KprobeEvent kprobe_event = 542;
ParamSetValueCpmFtraceEvent param_set_value_cpm = 543;
+ DoSysOpenFtraceEvent do_sys_open = 544;
+ OpenExecFtraceEvent open_exec = 545;
}
}
diff --git a/protos/perfetto/trace/perfetto_trace.proto b/protos/perfetto/trace/perfetto_trace.proto
index 5e07a0c..6104064 100644
--- a/protos/perfetto/trace/perfetto_trace.proto
+++ b/protos/perfetto/trace/perfetto_trace.proto
@@ -8805,6 +8805,19 @@
// End of protos/perfetto/trace/ftrace/filemap.proto
+// Begin of protos/perfetto/trace/ftrace/fs.proto
+
+message DoSysOpenFtraceEvent {
+ optional string filename = 1;
+ optional int32 flags = 2;
+ optional int32 mode = 3;
+}
+message OpenExecFtraceEvent {
+ optional string filename = 1;
+}
+
+// End of protos/perfetto/trace/ftrace/fs.proto
+
// Begin of protos/perfetto/trace/ftrace/ftrace.proto
message PrintFtraceEvent {
@@ -11424,6 +11437,8 @@
DevfreqFrequencyFtraceEvent devfreq_frequency = 541;
KprobeEvent kprobe_event = 542;
ParamSetValueCpmFtraceEvent param_set_value_cpm = 543;
+ DoSysOpenFtraceEvent do_sys_open = 544;
+ OpenExecFtraceEvent open_exec = 545;
}
}
diff --git a/protos/perfetto/trace_processor/trace_processor.proto b/protos/perfetto/trace_processor/trace_processor.proto
index bfb2a1f..86b4918 100644
--- a/protos/perfetto/trace_processor/trace_processor.proto
+++ b/protos/perfetto/trace_processor/trace_processor.proto
@@ -155,6 +155,8 @@
StatusResult status = 210;
// For TPM_REGISTER_SQL_PACKAGE.
RegisterSqlPackageResult register_sql_package_result = 211;
+ // For TPM_FINALIZE_TRACE_DATA.
+ FinalizeDataResult finalize_data_result = 212;
}
// Previously: RawQueryArgs for TPM_QUERY_RAW_DEPRECATED
@@ -356,4 +358,8 @@
message RegisterSqlPackageResult {
optional string error = 1;
-}
\ No newline at end of file
+}
+
+message FinalizeDataResult {
+ optional string error = 1;
+}
diff --git a/python/perfetto/trace_processor/trace_processor.descriptor b/python/perfetto/trace_processor/trace_processor.descriptor
index 976fb9b..40cec35 100644
--- a/python/perfetto/trace_processor/trace_processor.descriptor
+++ b/python/perfetto/trace_processor/trace_processor.descriptor
Binary files differ
diff --git a/python/tools/check_ratchet.py b/python/tools/check_ratchet.py
index d53b0c3..a34ff08 100755
--- a/python/tools/check_ratchet.py
+++ b/python/tools/check_ratchet.py
@@ -36,7 +36,7 @@
from dataclasses import dataclass
-EXPECTED_ANY_COUNT = 52
+EXPECTED_ANY_COUNT = 51
EXPECTED_RUN_METRIC_COUNT = 4
ROOT_DIR = os.path.dirname(
diff --git a/src/base/time.cc b/src/base/time.cc
index e799542..ad971af 100644
--- a/src/base/time.cc
+++ b/src/base/time.cc
@@ -188,10 +188,15 @@
std::string GetTimeFmt(const std::string& fmt) {
time_t raw_time;
time(&raw_time);
- struct tm* local_tm;
- local_tm = localtime(&raw_time);
+ struct tm local_tm;
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+ PERFETTO_CHECK(localtime_s(&local_tm, &raw_time) == 0);
+#else
+ tzset();
+ PERFETTO_CHECK(localtime_r(&raw_time, &local_tm) != nullptr);
+#endif
char buf[128];
- PERFETTO_CHECK(strftime(buf, 80, fmt.c_str(), local_tm) > 0);
+ PERFETTO_CHECK(strftime(buf, 80, fmt.c_str(), &local_tm) > 0);
return buf;
}
diff --git a/src/tools/ftrace_proto_gen/event_list b/src/tools/ftrace_proto_gen/event_list
index 8c99f49..7eeadfb 100644
--- a/src/tools/ftrace_proto_gen/event_list
+++ b/src/tools/ftrace_proto_gen/event_list
@@ -537,3 +537,5 @@
sched/sched_wakeup_task_attr
devfreq/devfreq_frequency
cpm_trace/param_set_value_cpm
+fs/do_sys_open
+fs/open_exec
diff --git a/src/trace_processor/importers/ftrace/ftrace_descriptors.cc b/src/trace_processor/importers/ftrace/ftrace_descriptors.cc
index 53085f9..c9d3970 100644
--- a/src/trace_processor/importers/ftrace/ftrace_descriptors.cc
+++ b/src/trace_processor/importers/ftrace/ftrace_descriptors.cc
@@ -24,7 +24,7 @@
namespace trace_processor {
namespace {
-std::array<FtraceMessageDescriptor, 544> descriptors{{
+std::array<FtraceMessageDescriptor, 546> descriptors{{
{nullptr, 0, {}},
{nullptr, 0, {}},
{nullptr, 0, {}},
@@ -6016,6 +6016,24 @@
{"timestamp", ProtoSchemaType::kInt64},
},
},
+ {
+ "do_sys_open",
+ 3,
+ {
+ {},
+ {"filename", ProtoSchemaType::kString},
+ {"flags", ProtoSchemaType::kInt32},
+ {"mode", ProtoSchemaType::kInt32},
+ },
+ },
+ {
+ "open_exec",
+ 1,
+ {
+ {},
+ {"filename", ProtoSchemaType::kString},
+ },
+ },
}};
} // namespace
diff --git a/src/trace_processor/importers/proto/android_probes_parser.cc b/src/trace_processor/importers/proto/android_probes_parser.cc
index 2da16c9..7c3e9f9 100644
--- a/src/trace_processor/importers/proto/android_probes_parser.cc
+++ b/src/trace_processor/importers/proto/android_probes_parser.cc
@@ -147,8 +147,10 @@
TrackTracker::Group::kPower, batt_power_id);
auto current = evt.current_ua();
auto voltage = evt.voltage_uv();
- context_->event_tracker->PushCounter(
- ts, static_cast<double>(current * voltage / 1000000000), track);
+ // Current is negative when discharging, but we want the power counter to
+ // always be positive, so take the absolute value.
+ auto power = std::abs(static_cast<double>(current * voltage / 1000000000));
+ context_->event_tracker->PushCounter(ts, power, track);
}
}
diff --git a/src/trace_processor/metrics/sql/chrome/BUILD.gn b/src/trace_processor/metrics/sql/chrome/BUILD.gn
index 30962b8..7d10aeb 100644
--- a/src/trace_processor/metrics/sql/chrome/BUILD.gn
+++ b/src/trace_processor/metrics/sql/chrome/BUILD.gn
@@ -26,6 +26,7 @@
"chrome_args_class_names.sql",
"chrome_event_metadata.sql",
"chrome_histogram_hashes.sql",
+ "chrome_histogram_summaries.sql",
"chrome_input_to_browser_intervals.sql",
"chrome_input_to_browser_intervals_base.sql",
"chrome_input_to_browser_intervals_template.sql",
diff --git a/src/trace_processor/metrics/sql/chrome/chrome_histogram_summaries.sql b/src/trace_processor/metrics/sql/chrome/chrome_histogram_summaries.sql
new file mode 100644
index 0000000..7946491
--- /dev/null
+++ b/src/trace_processor/metrics/sql/chrome/chrome_histogram_summaries.sql
@@ -0,0 +1,49 @@
+--
+-- Copyright 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
+--
+-- https://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.
+--
+
+INCLUDE PERFETTO MODULE chrome.histograms;
+
+DROP VIEW IF EXISTS HistogramSummaryTable;
+CREATE PERFETTO VIEW HistogramSummaryTable AS
+SELECT
+ hist.name AS histname,
+ CAST(AVG(hist.value) AS INTEGER) AS mean_histval,
+ COUNT(*) AS hist_count,
+ CAST(SUM(hist.value) AS INTEGER) AS sum_histval,
+ CAST(MAX(hist.value) AS INTEGER) AS max_histval,
+ CAST(PERCENTILE(hist.value, 90) AS INTEGER) AS p90_histval,
+ CAST(PERCENTILE(hist.value, 50) AS INTEGER) AS p50_histval
+FROM chrome_histograms hist
+GROUP BY hist.name;
+
+DROP VIEW IF EXISTS chrome_histogram_summaries_output;
+CREATE PERFETTO VIEW chrome_histogram_summaries_output AS
+SELECT ChromeHistogramSummaries(
+ 'histogram_summary', (
+ SELECT RepeatedField(
+ HistogramSummary(
+ 'name', histname,
+ 'mean', mean_histval,
+ 'count', hist_count,
+ 'sum', sum_histval,
+ 'max', max_histval,
+ 'p90', p90_histval,
+ 'p50', p50_histval
+ )
+ )
+ FROM HistogramSummaryTable
+ )
+);
diff --git a/src/trace_processor/rpc/rpc.cc b/src/trace_processor/rpc/rpc.cc
index 49feef3..d26cdc1 100644
--- a/src/trace_processor/rpc/rpc.cc
+++ b/src/trace_processor/rpc/rpc.cc
@@ -212,7 +212,11 @@
}
case RpcProto::TPM_FINALIZE_TRACE_DATA: {
Response resp(tx_seq_id_++, req_type);
- NotifyEndOfFile();
+ auto* result = resp->set_finalize_data_result();
+ base::Status res = NotifyEndOfFile();
+ if (!res.ok()) {
+ result->set_error(res.message());
+ }
resp.Send(rpc_response_fn_);
break;
}
diff --git a/src/traced/probes/ftrace/event_info.cc b/src/traced/probes/ftrace/event_info.cc
index 2cd9b20..f93e6e4 100644
--- a/src/traced/probes/ftrace/event_info.cc
+++ b/src/traced/probes/ftrace/event_info.cc
@@ -5045,6 +5045,32 @@
kUnsetFtraceId,
98,
kUnsetSize},
+ {"do_sys_open",
+ "fs",
+ {
+ {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+ "filename", 1, ProtoSchemaType::kString,
+ TranslationStrategy::kInvalidTranslationStrategy},
+ {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+ "flags", 2, ProtoSchemaType::kInt32,
+ TranslationStrategy::kInvalidTranslationStrategy},
+ {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+ "mode", 3, ProtoSchemaType::kInt32,
+ TranslationStrategy::kInvalidTranslationStrategy},
+ },
+ kUnsetFtraceId,
+ 544,
+ kUnsetSize},
+ {"open_exec",
+ "fs",
+ {
+ {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+ "filename", 1, ProtoSchemaType::kString,
+ TranslationStrategy::kInvalidTranslationStrategy},
+ },
+ kUnsetFtraceId,
+ 545,
+ kUnsetSize},
{"print",
"ftrace",
{
diff --git a/src/traced/probes/ftrace/test/data/synthetic/events/fs/do_sys_open/format b/src/traced/probes/ftrace/test/data/synthetic/events/fs/do_sys_open/format
new file mode 100644
index 0000000..d6bac92
--- /dev/null
+++ b/src/traced/probes/ftrace/test/data/synthetic/events/fs/do_sys_open/format
@@ -0,0 +1,13 @@
+name: do_sys_open
+ID: 685
+format:
+ field:unsigned short common_type; offset:0; size:2; signed:0;
+ field:unsigned char common_flags; offset:2; size:1; signed:0;
+ field:unsigned char common_preempt_count; offset:3; size:1; signed:0;
+ field:int common_pid; offset:4; size:4; signed:1;
+
+ field:__data_loc char[] filename; offset:8; size:4; signed:1;
+ field:int flags; offset:12; size:4; signed:1;
+ field:int mode; offset:16; size:4; signed:1;
+
+print fmt: ""%s" %x %o", __get_str(filename), REC->flags, REC->mode
diff --git a/src/traced/probes/ftrace/test/data/synthetic/events/fs/open_exec/format b/src/traced/probes/ftrace/test/data/synthetic/events/fs/open_exec/format
new file mode 100644
index 0000000..9f6fe3d
--- /dev/null
+++ b/src/traced/probes/ftrace/test/data/synthetic/events/fs/open_exec/format
@@ -0,0 +1,11 @@
+name: open_exec
+ID: 686
+format:
+ field:unsigned short common_type; offset:0; size:2; signed:0;
+ field:unsigned char common_flags; offset:2; size:1; signed:0;
+ field:unsigned char common_preempt_count; offset:3; size:1; signed:0;
+ field:int common_pid; offset:4; size:4; signed:1;
+
+ field:__data_loc char[] filename; offset:8; size:4; signed:1;
+
+print fmt: ""%s"", __get_str(filename)
diff --git a/test/trace_processor/diff_tests/parser/power/tests_linux_sysfs_power.py b/test/trace_processor/diff_tests/parser/power/tests_linux_sysfs_power.py
index 046d1c4..ccdae4f 100644
--- a/test/trace_processor/diff_tests/parser/power/tests_linux_sysfs_power.py
+++ b/test/trace_processor/diff_tests/parser/power/tests_linux_sysfs_power.py
@@ -147,7 +147,7 @@
packet {
timestamp: 4000000
battery {
- current_ua: 510000
+ current_ua: -510000
voltage_uv: 12000000
}
}
diff --git a/ui/src/core/app_impl.ts b/ui/src/core/app_impl.ts
index 7662755..c1045e2 100644
--- a/ui/src/core/app_impl.ts
+++ b/ui/src/core/app_impl.ts
@@ -214,8 +214,8 @@
return this.appCtx.currentTrace?.forPlugin(this.pluginId);
}
- scheduleFullRedraw(): void {
- raf.scheduleFullRedraw();
+ scheduleFullRedraw(force?: 'force'): void {
+ raf.scheduleFullRedraw(force);
}
get httpRpc() {
diff --git a/ui/src/core/command_manager.ts b/ui/src/core/command_manager.ts
index fdf6ee6..ad0f482 100644
--- a/ui/src/core/command_manager.ts
+++ b/ui/src/core/command_manager.ts
@@ -15,6 +15,7 @@
import {FuzzyFinder, FuzzySegment} from '../base/fuzzy';
import {Registry} from '../base/registry';
import {Command, CommandManager} from '../public/command';
+import {raf} from './raf_scheduler';
export interface CommandWithMatchInfo extends Command {
segments: FuzzySegment[];
@@ -39,10 +40,11 @@
return this.registry.register(cmd);
}
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
- runCommand(id: string, ...args: any[]): any {
+ runCommand(id: string, ...args: unknown[]): unknown {
const cmd = this.registry.get(id);
- return cmd.callback(...args);
+ const res = cmd.callback(...args);
+ Promise.resolve(res).finally(() => raf.scheduleFullRedraw('force'));
+ return res;
}
// Returns a list of commands that match the search term, along with a list
diff --git a/ui/src/core/load_trace.ts b/ui/src/core/load_trace.ts
index c39245f..41196c7 100644
--- a/ui/src/core/load_trace.ts
+++ b/ui/src/core/load_trace.ts
@@ -147,7 +147,7 @@
ftraceDropUntilAllCpusValid: FTRACE_DROP_UNTIL_FLAG.get(),
});
}
- engine.onResponseReceived = () => raf.scheduleFullRedraw();
+ engine.onResponseReceived = () => raf.scheduleFullRedraw('force');
if (isMetatracingEnabled()) {
engine.enableMetatrace(assertExists(getEnabledMetatracingCategories()));
diff --git a/ui/src/core/raf_scheduler.ts b/ui/src/core/raf_scheduler.ts
index b23379f..2935963 100644
--- a/ui/src/core/raf_scheduler.ts
+++ b/ui/src/core/raf_scheduler.ts
@@ -13,10 +13,19 @@
// limitations under the License.
import {PerfStats} from './perf_stats';
+import m from 'mithril';
+import {featureFlags} from './feature_flags';
export type AnimationCallback = (lastFrameMs: number) => void;
export type RedrawCallback = () => void;
+export const AUTOREDRAW_FLAG = featureFlags.register({
+ id: 'mithrilAutoredraw',
+ name: 'Enable Mithril autoredraw',
+ description: 'Turns calls to schedulefullRedraw() a no-op',
+ defaultValue: false,
+});
+
// This class orchestrates all RAFs in the UI. It ensures that there is only
// one animation frame handler overall and that callbacks are called in
// predictable order. There are two types of callbacks here:
@@ -34,12 +43,12 @@
// These happen at the end of full (DOM) animation frames.
private postRedrawCallbacks = new Array<RedrawCallback>();
- private syncDomRedrawFn: () => void = () => {};
private hasScheduledNextFrame = false;
private requestedFullRedraw = false;
private isRedrawing = false;
private _shutdown = false;
private recordPerfStats = false;
+ private mounts = new Map<Element, m.ComponentTypes>();
readonly perfStats = {
rafActions: new PerfStats(),
@@ -49,16 +58,23 @@
domRedraw: new PerfStats(),
};
- // Called by frontend/index.ts. syncDomRedrawFn is a function that invokes
- // m.render() of the root UiMain component.
- initialize(syncDomRedrawFn: () => void) {
- this.syncDomRedrawFn = syncDomRedrawFn;
+ constructor() {
+ // Patch m.redraw() to our RAF full redraw.
+ const origSync = m.redraw.sync;
+ const redrawFn = () => this.scheduleFullRedraw('force');
+ redrawFn.sync = origSync;
+ m.redraw = redrawFn;
+
+ m.mount = this.mount.bind(this);
}
// Schedule re-rendering of virtual DOM and canvas.
// If a callback is passed it will be executed after the DOM redraw has
// completed.
- scheduleFullRedraw(cb?: RedrawCallback) {
+ scheduleFullRedraw(force?: 'force', cb?: RedrawCallback) {
+ // If we are using autoredraw mode, make this function a no-op unless
+ // 'force' is passed.
+ if (AUTOREDRAW_FLAG.get() && force !== 'force') return;
this.requestedFullRedraw = true;
cb && this.postRedrawCallbacks.push(cb);
this.maybeScheduleAnimationFrame(true);
@@ -88,6 +104,16 @@
};
}
+ mount(element: Element, component: m.ComponentTypes | null): void {
+ const mounts = this.mounts;
+ if (component === null) {
+ mounts.delete(element);
+ } else {
+ mounts.set(element, component);
+ }
+ this.syncDomRedrawMountEntry(element, component);
+ }
+
shutdown() {
this._shutdown = true;
}
@@ -103,12 +129,37 @@
private syncDomRedraw() {
const redrawStart = performance.now();
- this.syncDomRedrawFn();
+
+ for (const [element, component] of this.mounts.entries()) {
+ this.syncDomRedrawMountEntry(element, component);
+ }
+
if (this.recordPerfStats) {
this.perfStats.domRedraw.addValue(performance.now() - redrawStart);
}
}
+ private syncDomRedrawMountEntry(
+ element: Element,
+ component: m.ComponentTypes | null,
+ ) {
+ // Mithril's render() function takes a third argument which tells us if a
+ // further redraw is needed (e.g. due to managed event handler). This allows
+ // us to implement auto-redraw. The redraw argument is documented in the
+ // official Mithril docs but is just not part of the @types/mithril package.
+ const mithrilRender = m.render as (
+ el: Element,
+ vnodes: m.Children,
+ redraw?: () => void,
+ ) => void;
+
+ mithrilRender(
+ element,
+ component !== null ? m(component) : null,
+ AUTOREDRAW_FLAG.get() ? () => raf.scheduleFullRedraw('force') : undefined,
+ );
+ }
+
private syncCanvasRedraw() {
const redrawStart = performance.now();
if (this.isRedrawing) return;
diff --git a/ui/src/frontend/error_dialog.ts b/ui/src/frontend/error_dialog.ts
index 99d4157..3e27b07 100644
--- a/ui/src/frontend/error_dialog.ts
+++ b/ui/src/frontend/error_dialog.ts
@@ -242,7 +242,7 @@
this.uploadStatus = '';
const uploader = new GcsUploader(this.traceData, {
onProgress: () => {
- raf.scheduleFullRedraw();
+ raf.scheduleFullRedraw('force');
this.uploadStatus = uploader.getEtaString();
if (uploader.state === 'UPLOADED') {
this.traceState = 'UPLOADED';
diff --git a/ui/src/frontend/help_modal.ts b/ui/src/frontend/help_modal.ts
index 819f271..322748e 100644
--- a/ui/src/frontend/help_modal.ts
+++ b/ui/src/frontend/help_modal.ts
@@ -54,7 +54,7 @@
nativeKeyboardLayoutMap()
.then((keyMap: KeyboardLayoutMap) => {
this.keyMap = keyMap;
- AppImpl.instance.scheduleFullRedraw();
+ AppImpl.instance.scheduleFullRedraw('force');
})
.catch((e) => {
if (
@@ -69,7 +69,7 @@
// The alternative would be to show key mappings for all keyboard
// layouts which is not feasible.
this.keyMap = new EnglishQwertyKeyboardLayoutMap();
- AppImpl.instance.scheduleFullRedraw();
+ AppImpl.instance.scheduleFullRedraw('force');
} else {
// Something unexpected happened. Either the browser doesn't conform
// to the keyboard API spec, or the keyboard API spec has changed!
diff --git a/ui/src/frontend/index.ts b/ui/src/frontend/index.ts
index 4c87e4d..600f08b 100644
--- a/ui/src/frontend/index.ts
+++ b/ui/src/frontend/index.ts
@@ -63,7 +63,7 @@
});
function routeChange(route: Route) {
- raf.scheduleFullRedraw(() => {
+ raf.scheduleFullRedraw('force', () => {
if (route.fragment) {
// This needs to happen after the next redraw call. It's not enough
// to use setTimeout(..., 0); since that may occur before the
@@ -148,7 +148,7 @@
});
// Wire up raf for widgets.
- setScheduleFullRedraw(() => raf.scheduleFullRedraw());
+ setScheduleFullRedraw((force?: 'force') => raf.scheduleFullRedraw(force));
// Load the css. The load is asynchronous and the CSS is not ready by the time
// appendChild returns.
@@ -225,12 +225,8 @@
const router = new Router();
router.onRouteChanged = routeChange;
- raf.initialize(() =>
- m.render(
- document.body,
- m(UiMain, pages.renderPageForCurrentRoute(AppImpl.instance.trace)),
- ),
- );
+ // Mount the main mithril component. This also forces a sync render pass.
+ raf.mount(document.body, UiMain);
if (
(location.origin.startsWith('http://localhost:') ||
@@ -269,12 +265,6 @@
routeChange(route);
});
- // Force one initial render to get everything in place
- m.render(
- document.body,
- m(UiMain, AppImpl.instance.pages.renderPageForCurrentRoute(undefined)),
- );
-
// Initialize plugins, now that we are ready to go.
const pluginManager = AppImpl.instance.plugins;
CORE_PLUGINS.forEach((p) => pluginManager.registerPlugin(p));
diff --git a/ui/src/frontend/omnibox.ts b/ui/src/frontend/omnibox.ts
index 5f1e29a..c94ee3f 100644
--- a/ui/src/frontend/omnibox.ts
+++ b/ui/src/frontend/omnibox.ts
@@ -328,7 +328,13 @@
document.removeEventListener('mousedown', this.onMouseDown);
}
+ // This is defined as an arrow function to have a single handler that can be
+ // added/remove while keeping `this` bound.
private onMouseDown = (e: Event) => {
+ // We need to schedule a redraw manually as this event handler was added
+ // manually to the DOM and doesn't use Mithril's auto-redraw system.
+ raf.scheduleFullRedraw('force');
+
// Don't close if the click was within ourselves or our popup.
if (e.target instanceof Node) {
if (this.popupElement && this.popupElement.contains(e.target)) {
diff --git a/ui/src/frontend/sidebar.ts b/ui/src/frontend/sidebar.ts
index 8ece2d3..9c03eb7 100644
--- a/ui/src/frontend/sidebar.ts
+++ b/ui/src/frontend/sidebar.ts
@@ -352,7 +352,9 @@
}
export class Sidebar implements m.ClassComponent<OptionalTraceImplAttrs> {
- private _redrawWhileAnimating = new Animation(() => raf.scheduleFullRedraw());
+ private _redrawWhileAnimating = new Animation(() =>
+ raf.scheduleFullRedraw('force'),
+ );
private _asyncJobPending = new Set<string>();
private _sectionExpanded = new Map<string, boolean>();
@@ -523,7 +525,7 @@
raf.scheduleFullRedraw();
res.finally(() => {
this._asyncJobPending.delete(itemId);
- raf.scheduleFullRedraw();
+ raf.scheduleFullRedraw('force');
});
};
}
diff --git a/ui/src/frontend/tab_panel.ts b/ui/src/frontend/tab_panel.ts
index 0662ef8..04aee7e 100644
--- a/ui/src/frontend/tab_panel.ts
+++ b/ui/src/frontend/tab_panel.ts
@@ -163,7 +163,7 @@
/* onDrag */ (_x, y) => {
const deltaYSinceDragStart = dragStartY - y;
this.resizableHeight = heightWhenDragStarted + deltaYSinceDragStart;
- raf.scheduleFullRedraw();
+ raf.scheduleFullRedraw('force');
},
/* onDragStarted */ (_x, y) => {
this.resizableHeight = this.height;
diff --git a/ui/src/frontend/ui_main.ts b/ui/src/frontend/ui_main.ts
index c67f5b7..b8d8dda 100644
--- a/ui/src/frontend/ui_main.ts
+++ b/ui/src/frontend/ui_main.ts
@@ -52,9 +52,9 @@
// This wrapper creates a new instance of UiMainPerTrace for each new trace
// loaded (including the case of no trace at the beginning).
export class UiMain implements m.ClassComponent {
- view({children}: m.CVnode) {
+ view() {
const currentTraceId = AppImpl.instance.trace?.engine.engineId ?? '';
- return [m(UiMainPerTrace, {key: currentTraceId}, children)];
+ return [m(UiMainPerTrace, {key: currentTraceId})];
}
}
@@ -629,12 +629,13 @@
this.maybeFocusOmnibar();
}
- view({children}: m.Vnode): m.Children {
+ view(): m.Children {
+ const app = AppImpl.instance;
const hotkeys: HotkeyConfig[] = [];
- for (const {id, defaultHotkey} of AppImpl.instance.commands.commands) {
+ for (const {id, defaultHotkey} of app.commands.commands) {
if (defaultHotkey) {
hotkeys.push({
- callback: () => AppImpl.instance.commands.runCommand(id),
+ callback: () => app.commands.runCommand(id),
hotkey: defaultHotkey,
});
}
@@ -650,10 +651,10 @@
omnibox: this.renderOmnibox(),
trace: this.trace,
}),
- children,
+ app.pages.renderPageForCurrentRoute(app.trace),
m(CookieConsent),
maybeRenderFullscreenModalDialog(),
- AppImpl.instance.perfDebugging.renderPerfStats(),
+ app.perfDebugging.renderPerfStats(),
),
);
}
diff --git a/ui/src/plugins/dev.perfetto.TimelineSync/index.ts b/ui/src/plugins/dev.perfetto.TimelineSync/index.ts
index aef7a76..37206a3 100644
--- a/ui/src/plugins/dev.perfetto.TimelineSync/index.ts
+++ b/ui/src/plugins/dev.perfetto.TimelineSync/index.ts
@@ -272,6 +272,7 @@
private onmessage(msg: MessageEvent) {
if (this._ctx === undefined) return; // Trace unloaded
if (!('perfettoSync' in msg.data)) return;
+ this._ctx.scheduleFullRedraw('force');
const msgData = msg.data as SyncMessage;
const sync = msgData.perfettoSync;
switch (sync.cmd) {
diff --git a/ui/src/plugins/dev.perfetto.WidgetsPage/widgets_page.ts b/ui/src/plugins/dev.perfetto.WidgetsPage/widgets_page.ts
index 327a179..a5ff6d7 100644
--- a/ui/src/plugins/dev.perfetto.WidgetsPage/widgets_page.ts
+++ b/ui/src/plugins/dev.perfetto.WidgetsPage/widgets_page.ts
@@ -685,6 +685,7 @@
icon: arg(icon, 'send'),
rightIcon: arg(rightIcon, 'arrow_forward'),
label: arg(label, 'Button', ''),
+ onclick: () => alert('button pressed'),
...rest,
}),
initialOpts: {
diff --git a/ui/src/public/app.ts b/ui/src/public/app.ts
index 50def57..0c8321b 100644
--- a/ui/src/public/app.ts
+++ b/ui/src/public/app.ts
@@ -54,7 +54,7 @@
// TODO(primiano): this should be needed in extremely rare cases. We should
// probably switch to mithril auto-redraw at some point.
- scheduleFullRedraw(): void;
+ scheduleFullRedraw(force?: 'force'): void;
/**
* Navigate to a new page.
diff --git a/ui/src/trace_processor/engine.ts b/ui/src/trace_processor/engine.ts
index ccb8a03..58a3705 100644
--- a/ui/src/trace_processor/engine.ts
+++ b/ui/src/trace_processor/engine.ts
@@ -208,7 +208,7 @@
let isFinalResponse = true;
switch (rpc.response) {
- case TPM.TPM_APPEND_TRACE_DATA:
+ case TPM.TPM_APPEND_TRACE_DATA: {
const appendResult = assertExists(rpc.appendResult);
const pendingPromise = assertExists(this.pendingParses.shift());
if (exists(appendResult.error) && appendResult.error.length > 0) {
@@ -217,9 +217,17 @@
pendingPromise.resolve();
}
break;
- case TPM.TPM_FINALIZE_TRACE_DATA:
- assertExists(this.pendingEOFs.shift()).resolve();
+ }
+ case TPM.TPM_FINALIZE_TRACE_DATA: {
+ const finalizeResult = assertExists(rpc.finalizeDataResult);
+ const pendingPromise = assertExists(this.pendingEOFs.shift());
+ if (exists(finalizeResult.error) && finalizeResult.error.length > 0) {
+ pendingPromise.reject(finalizeResult.error);
+ } else {
+ pendingPromise.resolve();
+ }
break;
+ }
case TPM.TPM_RESET_TRACE_PROCESSOR:
assertExists(this.pendingResetTraceProcessors.shift()).resolve();
break;
diff --git a/ui/src/widgets/editor.ts b/ui/src/widgets/editor.ts
index 58ea153..1187be0 100644
--- a/ui/src/widgets/editor.ts
+++ b/ui/src/widgets/editor.ts
@@ -21,6 +21,7 @@
import {assertExists} from '../base/logging';
import {DragGestureHandler} from '../base/drag_gesture_handler';
import {DisposableStack} from '../base/disposable_stack';
+import {scheduleFullRedraw} from './raf';
export interface EditorAttrs {
// Initial state for the editor.
@@ -64,6 +65,7 @@
text = selectedText;
}
onExecute(text);
+ scheduleFullRedraw('force');
return true;
},
});
@@ -75,6 +77,7 @@
view.update([tr]);
const text = view.state.doc.toString();
onUpdate(text);
+ scheduleFullRedraw('force');
};
}
diff --git a/ui/src/widgets/hotkey_context.ts b/ui/src/widgets/hotkey_context.ts
index f4d702a..767683e 100644
--- a/ui/src/widgets/hotkey_context.ts
+++ b/ui/src/widgets/hotkey_context.ts
@@ -14,6 +14,7 @@
import m from 'mithril';
import {checkHotkey, Hotkey} from '../base/hotkeys';
+import {scheduleFullRedraw} from './raf';
export interface HotkeyConfig {
hotkey: Hotkey;
@@ -58,6 +59,7 @@
if (checkHotkey(hotkey, e)) {
e.preventDefault();
callback();
+ scheduleFullRedraw('force');
}
});
}
diff --git a/ui/src/widgets/modal.ts b/ui/src/widgets/modal.ts
index e32f329..c07e6fe 100644
--- a/ui/src/widgets/modal.ts
+++ b/ui/src/widgets/modal.ts
@@ -14,8 +14,8 @@
import m from 'mithril';
import {defer} from '../base/deferred';
-import {scheduleFullRedraw} from './raf';
import {Icon} from './icon';
+import {scheduleFullRedraw} from './raf';
// This module deals with modal dialogs. Unlike most components, here we want to
// render the DOM elements outside of the corresponding vdom tree. For instance
@@ -79,7 +79,10 @@
export class Modal implements m.ClassComponent<ModalAttrs> {
onbeforeremove(vnode: m.VnodeDOM<ModalAttrs>) {
const removePromise = defer<void>();
- vnode.dom.addEventListener('animationend', () => removePromise.resolve());
+ vnode.dom.addEventListener('animationend', () => {
+ scheduleFullRedraw('force');
+ removePromise.resolve();
+ });
vnode.dom.classList.add('modal-fadeout');
// Retuning `removePromise` will cause Mithril to defer the actual component
@@ -94,7 +97,6 @@
// in turn will: (1) call the user's original attrs.onClose; (2) resolve
// the promise returned by showModal().
vnode.attrs.onClose();
- scheduleFullRedraw();
}
}
@@ -223,7 +225,7 @@
},
};
currentModal = attrs;
- scheduleFullRedraw();
+ redrawModal();
return returnedClosePromise;
}
@@ -232,7 +234,7 @@
// evident why a redraw is requested.
export function redrawModal() {
if (currentModal !== undefined) {
- scheduleFullRedraw();
+ scheduleFullRedraw('force');
}
}
@@ -251,7 +253,7 @@
return;
}
currentModal = undefined;
- scheduleFullRedraw();
+ scheduleFullRedraw('force');
}
export function getCurrentModalKey(): string | undefined {
diff --git a/ui/src/widgets/popup.ts b/ui/src/widgets/popup.ts
index ed16695..ac8b563 100644
--- a/ui/src/widgets/popup.ts
+++ b/ui/src/widgets/popup.ts
@@ -352,13 +352,13 @@
if (this.isOpen) {
this.isOpen = false;
this.onChange(this.isOpen);
- scheduleFullRedraw();
+ scheduleFullRedraw('force');
}
}
private togglePopup() {
this.isOpen = !this.isOpen;
this.onChange(this.isOpen);
- scheduleFullRedraw();
+ scheduleFullRedraw('force');
}
}
diff --git a/ui/src/widgets/portal.ts b/ui/src/widgets/portal.ts
index 734fee6..91c3608 100644
--- a/ui/src/widgets/portal.ts
+++ b/ui/src/widgets/portal.ts
@@ -46,13 +46,22 @@
export class Portal implements m.ClassComponent<PortalAttrs> {
private portalElement?: HTMLElement;
private containerElement?: Element;
+ private contentComponent: m.Component;
+
+ constructor({children}: m.CVnode<PortalAttrs>) {
+ // Create a temporary component that we can mount in oncreate, and unmount
+ // in onremove, but inject the new portal content (children) into it each
+ // render cycle. This is initialized here rather than in oncreate to avoid
+ // having to make it optional or use assertExists().
+ this.contentComponent = {view: () => children};
+ }
view() {
// Dummy element renders nothing but permits DOM access in lifecycle hooks.
return m('span', {style: {display: 'none'}});
}
- oncreate({attrs, children, dom}: m.VnodeDOM<PortalAttrs, this>) {
+ oncreate({attrs, dom}: m.CVnodeDOM<PortalAttrs>) {
const {
onContentMount = () => {},
onBeforeContentMount = (): MountOptions => ({}),
@@ -65,16 +74,21 @@
container.appendChild(this.portalElement);
this.applyPortalProps(attrs);
- m.render(this.portalElement, children);
+ m.mount(this.portalElement, this.contentComponent);
onContentMount(this.portalElement);
}
- onupdate({attrs, children}: m.VnodeDOM<PortalAttrs, this>) {
+ onbeforeupdate({children}: m.CVnode<PortalAttrs>) {
+ // Update the mounted content's view function to return the latest portal
+ // content passed in via children, without changing the component itself.
+ this.contentComponent.view = () => children;
+ }
+
+ onupdate({attrs}: m.CVnodeDOM<PortalAttrs>) {
const {onContentUpdate = () => {}} = attrs;
if (this.portalElement) {
this.applyPortalProps(attrs);
- m.render(this.portalElement, children);
onContentUpdate(this.portalElement);
}
}
@@ -86,14 +100,14 @@
}
}
- onremove({attrs}: m.VnodeDOM<PortalAttrs, this>) {
+ onremove({attrs}: m.CVnodeDOM<PortalAttrs>) {
const {onContentUnmount = () => {}} = attrs;
const container = this.containerElement ?? document.body;
if (this.portalElement) {
if (container.contains(this.portalElement)) {
onContentUnmount(this.portalElement);
// Rendering null ensures previous vnodes are removed properly.
- m.render(this.portalElement, null);
+ m.mount(this.portalElement, null);
container.removeChild(this.portalElement);
}
}
diff --git a/ui/src/widgets/raf.ts b/ui/src/widgets/raf.ts
index 20afb61..dc0d3ab 100644
--- a/ui/src/widgets/raf.ts
+++ b/ui/src/widgets/raf.ts
@@ -12,12 +12,12 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-let FULL_REDRAW_FUNCTION = () => {};
+let FULL_REDRAW_FUNCTION = (_force?: 'force') => {};
export function setScheduleFullRedraw(func: () => void) {
FULL_REDRAW_FUNCTION = func;
}
-export function scheduleFullRedraw() {
- FULL_REDRAW_FUNCTION();
+export function scheduleFullRedraw(force?: 'force') {
+ FULL_REDRAW_FUNCTION(force);
}
diff --git a/ui/src/widgets/vega_view.ts b/ui/src/widgets/vega_view.ts
index 7cbf533..1a8cb43 100644
--- a/ui/src/widgets/vega_view.ts
+++ b/ui/src/widgets/vega_view.ts
@@ -228,7 +228,7 @@
}
this._status = Status.Done;
this.pending = undefined;
- scheduleFullRedraw();
+ scheduleFullRedraw('force');
}
private handleError(pending: Promise<vega.View>, err: unknown) {
@@ -242,7 +242,7 @@
private setError(err: unknown) {
this._status = Status.Error;
this._error = getErrorMessage(err);
- scheduleFullRedraw();
+ scheduleFullRedraw('force');
}
[Symbol.dispose]() {
diff --git a/ui/src/widgets/virtual_scroll_helper.ts b/ui/src/widgets/virtual_scroll_helper.ts
index 475c360..6172c94 100644
--- a/ui/src/widgets/virtual_scroll_helper.ts
+++ b/ui/src/widgets/virtual_scroll_helper.ts
@@ -14,6 +14,7 @@
import {DisposableStack} from '../base/disposable_stack';
import {Bounds2D, Rect2D} from '../base/geom';
+import {scheduleFullRedraw} from './raf';
export interface VirtualScrollHelperOpts {
overdrawPx: number;
@@ -46,6 +47,7 @@
this._data.forEach((data) =>
recalculatePuckRect(sliderElement, containerElement, data),
);
+ scheduleFullRedraw('force');
};
containerElement.addEventListener('scroll', recalculateRects, {