Merge "gn: fix failing security checks in binskip" into main
diff --git a/Android.bp b/Android.bp
index a356ed1..f58dc80 100644
--- a/Android.bp
+++ b/Android.bp
@@ -11796,6 +11796,7 @@
"src/trace_processor/perfetto_sql/stdlib/intervals/overlap.sql",
"src/trace_processor/perfetto_sql/stdlib/linux/cpu_idle.sql",
"src/trace_processor/perfetto_sql/stdlib/pkvm/hypervisor.sql",
+ "src/trace_processor/perfetto_sql/stdlib/prelude/slices.sql",
"src/trace_processor/perfetto_sql/stdlib/sched/thread_level_parallelism.sql",
],
cmd: "$(location tools/gen_amalgamated_sql.py) --namespace=stdlib --cpp-out=$(out) $(in)",
diff --git a/BUILD b/BUILD
index 91da2de..fae1e4b 100644
--- a/BUILD
+++ b/BUILD
@@ -2305,6 +2305,14 @@
],
)
+# GN target: //src/trace_processor/perfetto_sql/stdlib/prelude:prelude
+perfetto_filegroup(
+ name = "src_trace_processor_perfetto_sql_stdlib_prelude_prelude",
+ srcs = [
+ "src/trace_processor/perfetto_sql/stdlib/prelude/slices.sql",
+ ],
+)
+
# GN target: //src/trace_processor/perfetto_sql/stdlib/sched:sched
perfetto_filegroup(
name = "src_trace_processor_perfetto_sql_stdlib_sched_sched",
@@ -2325,6 +2333,7 @@
":src_trace_processor_perfetto_sql_stdlib_intervals_intervals",
":src_trace_processor_perfetto_sql_stdlib_linux_linux",
":src_trace_processor_perfetto_sql_stdlib_pkvm_pkvm",
+ ":src_trace_processor_perfetto_sql_stdlib_prelude_prelude",
":src_trace_processor_perfetto_sql_stdlib_sched_sched",
],
outs = [
diff --git a/infra/perfetto.dev/src/gen_stdlib_docs_md.py b/infra/perfetto.dev/src/gen_stdlib_docs_md.py
index 3a61b37..b0ed5c7 100644
--- a/infra/perfetto.dev/src/gen_stdlib_docs_md.py
+++ b/infra/perfetto.dev/src/gen_stdlib_docs_md.py
@@ -47,6 +47,24 @@
long_s = []
long_s.append(f'## Module: {self.module_name}')
+ if self.module_name == 'prelude':
+ # Prelude is a special module which is automatically imported and doesn't
+ # have any include keys.
+ objs = '\n'.join(obj for file in self.files_md for obj in file.objs)
+ if objs:
+ long_s.append('#### Views/Tables')
+ long_s.append(objs)
+ funs = '\n'.join(fun for file in self.files_md for fun in file.funs)
+ if funs:
+ long_s.append('#### Functions')
+ long_s.append(funs)
+ table_funs = '\n'.join(
+ view_fun for file in self.files_md for view_fun in file.view_funs)
+ if table_funs:
+ long_s.append('#### Table Functions')
+ long_s.append(table_funs)
+ return '\n'.join(long_s)
+
for file in self.files_md:
if not file.objs and not file.funs and not file.view_funs:
continue
@@ -70,6 +88,7 @@
def __init__(self, module_name, file_dict):
self.import_key = file_dict['import_key']
+ import_key_name = self.import_key if module_name != 'prelude' else 'N/A'
self.objs, self.funs, self.view_funs = [], [], []
summary_objs_list, summary_funs_list, summary_view_funs_list = [], [], []
@@ -81,7 +100,7 @@
# Add summary of imported view/table
desc = data['desc'].split('.')[0]
summary_objs_list.append(f'''[{data['name']}](#{anchor})|'''
- f'''{file_dict['import_key']}|'''
+ f'''{import_key_name}|'''
f'''{desc}''')
self.objs.append(f'''\n\n<a name="{anchor}"></a>'''
@@ -102,7 +121,7 @@
# Add summary of imported function
summary_funs_list.append(f'''[{data['name']}](#{anchor})|'''
- f'''{file_dict['import_key']}|'''
+ f'''{import_key_name}|'''
f'''{data['return_type']}|'''
f'''{data['desc'].split('.')[0]}''')
self.funs.append(
@@ -125,7 +144,7 @@
anchor = rf'''view_fun/{module_name}/{data['name']}'''
# Add summary of imported view function
summary_view_funs_list.append(f'''[{data['name']}](#{anchor})|'''
- f'''{file_dict['import_key']}|'''
+ f'''{import_key_name}|'''
f'''{data['desc'].split('.')[0]}''')
self.view_funs.append(f'''\n\n<a name="{anchor}"></a>'''
@@ -165,6 +184,7 @@
modules_dict[module_name] = ModuleMd(module_name, module_files)
common_module = modules_dict.pop('common')
+ prelude_module = modules_dict.pop('prelude')
with open(args.output, 'w') as f:
f.write('''
@@ -197,6 +217,9 @@
FROM android_startups;
```
+Prelude is a special module is automatically imported. It contains key helper
+tables, views and functions which are universally useful.
+
More information on importing modules is available in the
[syntax documentation](/docs/analysis/perfetto-sql-syntax#including-perfettosql-modules)
for the `INCLUDE PERFETTO MODULE` statement.
@@ -206,23 +229,29 @@
## Summary
''')
- summary_objs = [common_module.summary_objs
- ] if common_module.summary_objs else []
+ summary_objs = [prelude_module.summary_objs
+ ] if prelude_module.summary_objs else []
+ summary_objs += [common_module.summary_objs
+ ] if common_module.summary_objs else []
summary_objs += [
module.summary_objs
for name, module in modules_dict.items()
if (module.summary_objs and name != 'experimental')
]
- summary_funs = [common_module.summary_funs
- ] if common_module.summary_funs else []
+ summary_funs = [prelude_module.summary_funs
+ ] if prelude_module.summary_funs else []
+ summary_funs += [common_module.summary_funs
+ ] if common_module.summary_funs else []
summary_funs += [
module.summary_funs
for name, module in modules_dict.items()
if (module.summary_funs and name != 'experimental')
]
- summary_view_funs = [common_module.summary_view_funs
- ] if common_module.summary_view_funs else []
+ summary_view_funs = [prelude_module.summary_view_funs
+ ] if prelude_module.summary_view_funs else []
+ summary_view_funs += [common_module.summary_view_funs
+ ] if common_module.summary_view_funs else []
summary_view_funs += [
module.summary_view_funs
for name, module in modules_dict.items()
@@ -251,6 +280,8 @@
f.write('\n')
f.write('\n\n')
+ f.write(prelude_module.print_description())
+ f.write('\n')
f.write(common_module.print_description())
f.write('\n')
f.write('\n'.join(
diff --git a/python/generators/sql_processing/docs_parse.py b/python/generators/sql_processing/docs_parse.py
index 1c3142d..981316d 100644
--- a/python/generators/sql_processing/docs_parse.py
+++ b/python/generators/sql_processing/docs_parse.py
@@ -68,10 +68,11 @@
if upper:
module_pattern = module_pattern.upper()
starts_with_module_name = re.match(module_pattern, self.name, re.IGNORECASE)
- if self.module == "common":
+ if self.module == "common" or self.module == "prelude":
if starts_with_module_name:
- self._error('Names of tables/views/functions in the "common" module '
- f'should not start with {module_pattern}')
+ self._error(
+ 'Names of tables/views/functions in the "{self.module}" module '
+ f'should not start with {module_pattern}')
return self.name
if not starts_with_module_name:
self._error('Names of tables/views/functions should be prefixed with the '
diff --git a/src/perfetto_cmd/perfetto_cmd.cc b/src/perfetto_cmd/perfetto_cmd.cc
index 323d7bc..2667354 100644
--- a/src/perfetto_cmd/perfetto_cmd.cc
+++ b/src/perfetto_cmd/perfetto_cmd.cc
@@ -600,6 +600,7 @@
trace_config_.reset(new TraceConfig());
bool parsed = false;
+ bool cfg_could_be_txt = false;
const bool will_trace_or_trigger = !is_attach() && !query_service_;
if (!will_trace_or_trigger) {
if ((!trace_config_raw.empty() || has_config_options)) {
@@ -625,6 +626,14 @@
trace_config_.get());
} else {
parsed = trace_config_->ParseFromString(trace_config_raw);
+ cfg_could_be_txt =
+ !parsed && std::all_of(trace_config_raw.begin(),
+ trace_config_raw.end(), [](char c) {
+ // This is equiv to: isprint(c) || isspace(x)
+ // but doesn't depend on and load the locale.
+ return (c >= 32 && c <= 126) ||
+ (c >= 9 && c <= 13);
+ });
}
}
@@ -633,6 +642,12 @@
trace_config_raw.clear();
} else if (will_trace_or_trigger && !clone_tsid_) {
PERFETTO_ELOG("The trace config is invalid, bailing out.");
+ if (cfg_could_be_txt) {
+ PERFETTO_ELOG(
+ "Looks like you are passing a textual config but I'm expecting a "
+ "proto-encoded binary config.");
+ PERFETTO_ELOG("Try adding --txt to the cmdline.");
+ }
return 1;
}
diff --git a/src/protozero/protoc_plugin/cppgen_plugin.cc b/src/protozero/protoc_plugin/cppgen_plugin.cc
index a33803f..f134caa 100644
--- a/src/protozero/protoc_plugin/cppgen_plugin.cc
+++ b/src/protozero/protoc_plugin/cppgen_plugin.cc
@@ -27,11 +27,7 @@
#include <google/protobuf/compiler/code_generator.h>
#include <google/protobuf/compiler/importer.h>
#include <google/protobuf/compiler/plugin.h>
-#include <google/protobuf/dynamic_message.h>
#include <google/protobuf/io/printer.h>
-#include <google/protobuf/io/zero_copy_stream_impl.h>
-#include <google/protobuf/util/field_comparator.h>
-#include <google/protobuf/util/message_differencer.h>
#include "perfetto/ext/base/string_utils.h"
diff --git a/src/trace_processor/perfetto_sql/stdlib/BUILD.gn b/src/trace_processor/perfetto_sql/stdlib/BUILD.gn
index d9dc54d..c78d7f9 100644
--- a/src/trace_processor/perfetto_sql/stdlib/BUILD.gn
+++ b/src/trace_processor/perfetto_sql/stdlib/BUILD.gn
@@ -26,6 +26,7 @@
"intervals",
"linux",
"pkvm",
+ "prelude",
"sched",
]
generated_header = "stdlib.h"
diff --git a/src/trace_processor/perfetto_sql/stdlib/prelude/BUILD.gn b/src/trace_processor/perfetto_sql/stdlib/prelude/BUILD.gn
new file mode 100644
index 0000000..19162d5
--- /dev/null
+++ b/src/trace_processor/perfetto_sql/stdlib/prelude/BUILD.gn
@@ -0,0 +1,19 @@
+# Copyright (C) 2023 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.
+
+import("../../../../../gn/perfetto_sql.gni")
+
+perfetto_sql_source_set("prelude") {
+ sources = [ "slices.sql" ]
+}
diff --git a/src/trace_processor/perfetto_sql/stdlib/prelude/slices.sql b/src/trace_processor/perfetto_sql/stdlib/prelude/slices.sql
new file mode 100644
index 0000000..a669f04
--- /dev/null
+++ b/src/trace_processor/perfetto_sql/stdlib/prelude/slices.sql
@@ -0,0 +1,32 @@
+--
+-- Copyright 2023 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.
+
+-- Given two slice ids, returns whether the first is an ancestor of the second.
+CREATE PERFETTO FUNCTION slice_is_ancestor(
+ -- Id of the potential ancestor slice.
+ ancestor_id LONG,
+ -- Id of the potential descendant slice.
+ descendant_id LONG
+)
+-- Whether `ancestor_id` slice is an ancestor of `descendant_id`.
+RETURNS BOOL AS
+SELECT
+ ancestor.track_id = descendant.track_id AND
+ ancestor.ts <= descendant.ts AND
+ (ancestor.dur == -1 OR ancestor.ts + ancestor.dur >= descendant.ts + descendant.dur)
+FROM slice ancestor
+JOIN slice descendant
+WHERE ancestor.id = $ancestor_id
+ AND descendant.id = $descendant_id
\ No newline at end of file
diff --git a/src/trace_processor/trace_processor_impl.cc b/src/trace_processor/trace_processor_impl.cc
index 6c3b209..a24ac52 100644
--- a/src/trace_processor/trace_processor_impl.cc
+++ b/src/trace_processor/trace_processor_impl.cc
@@ -752,6 +752,7 @@
// Initalize the tables and views in the prelude.
InitializePreludeTablesViews(engine_->sqlite_engine()->db());
+ // Register stdlib modules.
auto stdlib_modules = GetStdlibModules();
for (auto module_it = stdlib_modules.GetIterator(); module_it; ++module_it) {
base::Status status =
@@ -920,6 +921,16 @@
*metric.proto_field_name);
}
}
+
+ // Import prelude module.
+ {
+ auto result = engine_->Execute(SqlSource::FromTraceProcessorImplementation(
+ "INCLUDE PERFETTO MODULE prelude.*"));
+ if (!result.status().ok()) {
+ PERFETTO_FATAL("Failed to import prelude: %s",
+ result.status().c_message());
+ }
+ }
}
namespace {
diff --git a/src/tracing/core/tracing_service_impl.cc b/src/tracing/core/tracing_service_impl.cc
index d975a29..1e4783a 100644
--- a/src/tracing/core/tracing_service_impl.cc
+++ b/src/tracing/core/tracing_service_impl.cc
@@ -2013,6 +2013,9 @@
if (!weak_this)
return;
TracingSession* session = weak_this->GetTracingSession(tsid);
+ if (!session) {
+ return;
+ }
session->final_flush_outcome = success
? TraceStats::FINAL_FLUSH_SUCCEEDED
: TraceStats::FINAL_FLUSH_FAILED;
diff --git a/src/tracing/core/tracing_service_impl_unittest.cc b/src/tracing/core/tracing_service_impl_unittest.cc
index fd578b8..c024c82 100644
--- a/src/tracing/core/tracing_service_impl_unittest.cc
+++ b/src/tracing/core/tracing_service_impl_unittest.cc
@@ -5158,4 +5158,55 @@
Eq("B|1023|payload"))))));
}
+// This is a regression test for https://b.corp.google.com/issues/307601836. The
+// test covers the case of a consumer disconnecting while the tracing session is
+// executing the final flush.
+TEST_F(TracingServiceImplTest, ConsumerDisconnectionRacesFlushAndDisable) {
+ std::unique_ptr<MockConsumer> consumer = CreateMockConsumer();
+ consumer->Connect(svc.get());
+
+ std::unique_ptr<MockProducer> producer = CreateMockProducer();
+ producer->Connect(svc.get(), "mock_producer");
+
+ producer->RegisterDataSource("ds");
+
+ TraceConfig trace_config;
+ trace_config.add_buffers()->set_size_kb(128);
+ auto* trigger_config = trace_config.mutable_trigger_config();
+ trigger_config->set_trigger_mode(TraceConfig::TriggerConfig::STOP_TRACING);
+ trigger_config->set_trigger_timeout_ms(100000);
+ auto* trigger = trigger_config->add_triggers();
+ trigger->set_name("trigger_name");
+ auto* ds_cfg = trace_config.add_data_sources()->mutable_config();
+ ds_cfg->set_name("ds");
+
+ consumer->EnableTracing(trace_config);
+ producer->WaitForTracingSetup();
+ producer->WaitForDataSourceSetup("ds");
+ producer->WaitForDataSourceStart("ds");
+
+ auto writer1 = producer->CreateTraceWriter("ds");
+
+ auto producer_flush_cb = [&](FlushRequestID flush_req_id,
+ const DataSourceInstanceID* /*id*/, size_t,
+ FlushFlags) {
+ // Notify the tracing service that the flush is complete.
+ producer->endpoint()->NotifyFlushComplete(flush_req_id);
+ // Also disconnect the consumer (this terminates the tracing session). The
+ // consumer disconnection is postponed with a PostTask(). The goal is to run
+ // the lambda inside TracingServiceImpl::FlushAndDisableTracing() with an
+ // empty `tracing_sessions_` map.
+ task_runner.PostTask([&]() { consumer.reset(); });
+ };
+ EXPECT_CALL(*producer, Flush(_, _, _, _)).WillOnce(Invoke(producer_flush_cb));
+
+ // Cause the tracing session to stop. Note that
+ // TracingServiceImpl::FlushAndDisableTracing() is also called when
+ // duration_ms expires, but in a test it's faster to use a trigger.
+ producer->endpoint()->ActivateTriggers({"trigger_name"});
+ producer->WaitForDataSourceStop("ds");
+
+ task_runner.RunUntilIdle();
+}
+
} // namespace perfetto
diff --git a/test/trace_processor/diff_tests/include_index.py b/test/trace_processor/diff_tests/include_index.py
index 28c5abd..5def22b 100644
--- a/test/trace_processor/diff_tests/include_index.py
+++ b/test/trace_processor/diff_tests/include_index.py
@@ -99,6 +99,7 @@
from diff_tests.stdlib.prelude.math_functions_tests import PreludeMathFunctions
from diff_tests.stdlib.prelude.pprof_functions_tests import PreludePprofFunctions
from diff_tests.stdlib.prelude.window_functions_tests import PreludeWindowFunctions
+from diff_tests.stdlib.prelude.slices_tests import PreludeSlices
from diff_tests.stdlib.sched.tests import StdlibSched
from diff_tests.stdlib.slices.tests import Slices
from diff_tests.stdlib.span_join.tests_left_join import SpanJoinLeftJoin
@@ -232,6 +233,7 @@
*PreludeWindowFunctions(index_path, 'stdlib/prelude',
'PreludeWindowFunctions').fetch(),
*Pkvm(index_path, 'stdlib/pkvm', 'Pkvm').fetch(),
+ *PreludeSlices(index_path, 'stdlib/prelude', 'PreludeSlices').fetch(),
*StdlibSmoke(index_path, 'stdlib', 'StdlibSmoke').fetch(),
*StdlibCommon(index_path, 'stdlib/common', 'StdlibCommon').fetch(),
*Slices(index_path, 'stdlib/slices', 'Slices').fetch(),
diff --git a/test/trace_processor/diff_tests/stdlib/prelude/nested_slices_trace.py b/test/trace_processor/diff_tests/stdlib/prelude/nested_slices_trace.py
new file mode 100644
index 0000000..43cc76a
--- /dev/null
+++ b/test/trace_processor/diff_tests/stdlib/prelude/nested_slices_trace.py
@@ -0,0 +1,35 @@
+#!/usr/bin/env python3
+# Copyright (C) 2023 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 a
+#
+# 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.
+
+from os import sys
+
+import synth_common
+
+from synth_common import ms_to_ns
+
+trace = synth_common.create_trace()
+
+track1_id = 1
+track2_id = 2
+
+trace.add_track_descriptor(track1_id)
+trace.add_track_descriptor(track2_id)
+
+trace.add_track_event_slice("Slice 1", ts=1, dur=10, track=track1_id)
+trace.add_track_event_slice("Slice 2", ts=2, dur=3, track=track1_id)
+trace.add_track_event_slice("Slice 3", ts=6, dur=3, track=track1_id)
+trace.add_track_event_slice("Slice 4", ts=3, dur=1, track=track2_id)
+
+sys.stdout.buffer.write(trace.trace.SerializeToString())
diff --git a/test/trace_processor/diff_tests/stdlib/prelude/slices_tests.py b/test/trace_processor/diff_tests/stdlib/prelude/slices_tests.py
new file mode 100644
index 0000000..793b713
--- /dev/null
+++ b/test/trace_processor/diff_tests/stdlib/prelude/slices_tests.py
@@ -0,0 +1,43 @@
+#!/usr/bin/env python3
+# Copyright (C) 2023 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 a
+#
+# 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.
+
+from python.generators.diff_tests.testing import Path, DataPath, Metric
+from python.generators.diff_tests.testing import Csv, Json, TextProto, BinaryProto
+from python.generators.diff_tests.testing import DiffTestBlueprint
+from python.generators.diff_tests.testing import TestSuite
+from google.protobuf import text_format
+
+
+class PreludeSlices(TestSuite):
+
+ def test_slice_is_ancestor(self):
+ return DiffTestBlueprint(
+ trace=Path('nested_slices_trace.py'),
+ query="""
+ SELECT
+ s1.name, s2.name, slice_is_ancestor(s1.id, s2.id) AS is_ancestor
+ FROM slice s1
+ JOIN slice s2
+ WHERE s1.name < s2.name
+ """,
+ out=Csv("""
+ "name","name","is_ancestor"
+ "Slice 1","Slice 2",1
+ "Slice 1","Slice 4",0
+ "Slice 1","Slice 3",1
+ "Slice 2","Slice 4",0
+ "Slice 2","Slice 3",0
+ "Slice 3","Slice 4",0
+ """))
diff --git a/ui/src/controller/flow_events_controller.ts b/ui/src/controller/flow_events_controller.ts
index b5be573..28175b3 100644
--- a/ui/src/controller/flow_events_controller.ts
+++ b/ui/src/controller/flow_events_controller.ts
@@ -100,6 +100,7 @@
name: STR_NULL,
category: STR_NULL,
id: NUM,
+ flowToDescendant: NUM,
});
const nullToStr = (s: null|string): string => {
@@ -155,6 +156,7 @@
dur: it.endSliceStartTs - it.beginSliceEndTs,
category,
name,
+ flowToDescendant: !!it.flowToDescendant,
});
}
@@ -343,7 +345,8 @@
(process_in.name || ' ' || process_in.pid) as endProcessName,
extract_arg(f.arg_set_id, 'cat') as category,
extract_arg(f.arg_set_id, 'name') as name,
- f.id as id
+ f.id as id,
+ slice_is_ancestor(t1.slice_id, t2.slice_id) as flowToDescendant
from ${connectedFlows} f
join slice t1 on f.slice_out = t1.slice_id
join slice t2 on f.slice_in = t2.slice_id
@@ -417,7 +420,8 @@
NULL as endProcessName,
extract_arg(f.arg_set_id, 'cat') as category,
extract_arg(f.arg_set_id, 'name') as name,
- f.id as id
+ f.id as id,
+ slice_is_ancestor(t1.slice_id, t2.slice_id) as flowToDescendant
from flow f
join slice t1 on f.slice_out = t1.slice_id
join slice t2 on f.slice_in = t2.slice_id
diff --git a/ui/src/frontend/flow_events_renderer.ts b/ui/src/frontend/flow_events_renderer.ts
index f06704e..ed47282 100644
--- a/ui/src/frontend/flow_events_renderer.ts
+++ b/ui/src/frontend/flow_events_renderer.ts
@@ -214,8 +214,14 @@
endDir = endYConnection.y > beginYConnection.y ? 'DOWN' : 'UP';
}
+
const begin = {
- x: this.getXCoordinate(flow.begin.sliceEndTs),
+ // If the flow goes to a descendant, we want to draw the arrow from the
+ // beginning of the slice
+ // rather from the end to avoid the flow arrow going backwards.
+ x: this.getXCoordinate(
+ flow.flowToDescendant ? flow.begin.sliceStartTs :
+ flow.begin.sliceEndTs),
y: beginYConnection.y,
dir: beginDir,
};
diff --git a/ui/src/frontend/globals.ts b/ui/src/frontend/globals.ts
index 9a45e50..0affaed 100644
--- a/ui/src/frontend/globals.ts
+++ b/ui/src/frontend/globals.ts
@@ -124,6 +124,9 @@
end: FlowPoint;
dur: duration;
+ // Whether this flow connects a slice with its descendant.
+ flowToDescendant: boolean;
+
category?: string;
name?: string;
}
diff --git a/ui/src/frontend/thread_state_tab.ts b/ui/src/frontend/thread_state_tab.ts
index 35d3b9f..a75ca4b 100644
--- a/ui/src/frontend/thread_state_tab.ts
+++ b/ui/src/frontend/thread_state_tab.ts
@@ -309,7 +309,7 @@
experimental_thread_executing_span_critical_path(
${this.state?.thread?.utid},
trace_bounds.start_ts,
- trace_bounds.end_ts) cr,
+ trace_bounds.end_ts - trace_bounds.start_ts) cr,
trace_bounds
JOIN thread USING(utid)
JOIN process USING(upid)
@@ -334,7 +334,7 @@
experimental_thread_executing_span_critical_path_stack(
${this.state?.thread?.utid},
trace_bounds.start_ts,
- trace_bounds.end_ts) cr,
+ trace_bounds.end_ts - trace_bounds.start_ts) cr,
trace_bounds WHERE name IS NOT NULL
`,
columns: sliceColumnNames,