Merge "Add interning support to ProtoToArgsParser."
diff --git a/include/perfetto/ext/base/scoped_file.h b/include/perfetto/ext/base/scoped_file.h
index 978843e..5a99de6 100644
--- a/include/perfetto/ext/base/scoped_file.h
+++ b/include/perfetto/ext/base/scoped_file.h
@@ -53,8 +53,10 @@
class Checker = internal::DefaultValidityChecker<T, InvalidValue>>
class PERFETTO_EXPORT ScopedResource {
public:
- explicit ScopedResource(T t = InvalidValue) : t_(t) {}
+ using ValidityChecker = Checker;
static constexpr T kInvalid = InvalidValue;
+
+ explicit ScopedResource(T t = InvalidValue) : t_(t) {}
ScopedResource(ScopedResource&& other) noexcept {
t_ = other.t_;
other.t_ = InvalidValue;
diff --git a/include/perfetto/tracing/console_interceptor.h b/include/perfetto/tracing/console_interceptor.h
index 75fffc7..98c8879 100644
--- a/include/perfetto/tracing/console_interceptor.h
+++ b/include/perfetto/tracing/console_interceptor.h
@@ -57,7 +57,8 @@
struct ConsoleColor;
-class ConsoleInterceptor : public Interceptor<ConsoleInterceptor> {
+class PERFETTO_EXPORT ConsoleInterceptor
+ : public Interceptor<ConsoleInterceptor> {
public:
~ConsoleInterceptor() override;
diff --git a/include/perfetto/tracing/data_source.h b/include/perfetto/tracing/data_source.h
index cd35ef0..c4c6681 100644
--- a/include/perfetto/tracing/data_source.h
+++ b/include/perfetto/tracing/data_source.h
@@ -319,6 +319,11 @@
if (PERFETTO_UNLIKELY(!tls_state_))
tls_state_ = GetOrCreateDataSourceTLS(&static_state_);
+ // Avoid re-entering the trace point recursively.
+ if (PERFETTO_UNLIKELY(tls_state_->root_tls->is_in_trace_point))
+ return;
+ internal::ScopedReentrancyAnnotator scoped_annotator(*tls_state_->root_tls);
+
// TracingTLS::generation is a global monotonic counter that is incremented
// every time a tracing session is stopped. We use that as a signal to force
// a slow-path garbage collection of all the trace writers for the current
@@ -373,12 +378,6 @@
// handshaking to make this extremely unrealistic.
auto& tls_inst = tls_state_->per_instance[i];
-
- // Avoid re-entering the trace point recursively.
- if (PERFETTO_UNLIKELY(tls_inst.is_in_trace_point))
- continue;
- ScopedReentrancyAnnotator scoped_annotator(tls_inst);
-
if (PERFETTO_UNLIKELY(!tls_inst.trace_writer)) {
// Here we need an acquire barrier, which matches the release-store made
// by TracingMuxerImpl::SetupDataSource(), to ensure that the backend_id
@@ -456,18 +455,6 @@
}
};
- struct ScopedReentrancyAnnotator {
- ScopedReentrancyAnnotator(
- internal::DataSourceInstanceThreadLocalState& tls_inst)
- : tls_inst_(tls_inst) {
- tls_inst_.is_in_trace_point = true;
- }
- ~ScopedReentrancyAnnotator() { tls_inst_.is_in_trace_point = false; }
-
- private:
- internal::DataSourceInstanceThreadLocalState& tls_inst_;
- };
-
// Create the user provided incremental state in the given thread-local
// storage. Note: The second parameter here is used to specialize the case
// where there is no incremental state type.
diff --git a/include/perfetto/tracing/internal/data_source_internal.h b/include/perfetto/tracing/internal/data_source_internal.h
index 6f19db0..cb052c6 100644
--- a/include/perfetto/tracing/internal/data_source_internal.h
+++ b/include/perfetto/tracing/internal/data_source_internal.h
@@ -156,7 +156,6 @@
data_source_instance_id = 0;
incremental_state_generation = 0;
is_intercepted = false;
- is_in_trace_point = false;
}
std::unique_ptr<TraceWriterBase> trace_writer;
@@ -167,7 +166,6 @@
BufferId buffer_id;
uint64_t data_source_instance_id;
bool is_intercepted;
- bool is_in_trace_point;
};
// Per-DataSource-type thread-local state.
diff --git a/include/perfetto/tracing/internal/tracing_tls.h b/include/perfetto/tracing/internal/tracing_tls.h
index 67f0637..68515a4 100644
--- a/include/perfetto/tracing/internal/tracing_tls.h
+++ b/include/perfetto/tracing/internal/tracing_tls.h
@@ -74,6 +74,11 @@
// thread-local TraceWriter(s) is issued.
uint32_t generation = 0;
+ // This flag is true while this thread is inside a trace point for any data
+ // source or in other delicate parts of the tracing machinery during which we
+ // should not try to trace. Used to prevent unexpected re-entrancy.
+ bool is_in_trace_point = false;
+
// By default all data source instances have independent thread-local state
// (see above).
std::array<DataSourceThreadLocalState, kMaxDataSources> data_sources_tls{};
@@ -84,6 +89,17 @@
DataSourceThreadLocalState track_event_tls{};
};
+struct ScopedReentrancyAnnotator {
+ ScopedReentrancyAnnotator(TracingTLS& root_tls) : root_tls_(root_tls) {
+ PERFETTO_DCHECK(!root_tls_.is_in_trace_point);
+ root_tls_.is_in_trace_point = true;
+ }
+ ~ScopedReentrancyAnnotator() { root_tls_.is_in_trace_point = false; }
+
+ private:
+ TracingTLS& root_tls_;
+};
+
} // namespace internal
} // namespace perfetto
diff --git a/include/perfetto/tracing/internal/track_event_internal.h b/include/perfetto/tracing/internal/track_event_internal.h
index 8c4f20f..d293039 100644
--- a/include/perfetto/tracing/internal/track_event_internal.h
+++ b/include/perfetto/tracing/internal/track_event_internal.h
@@ -187,6 +187,8 @@
#endif
}
+ static int GetSessionCount();
+
// Represents the default track for the calling thread.
static const Track kDefaultTrack;
@@ -199,6 +201,8 @@
static protos::pbzero::DebugAnnotation* AddDebugAnnotation(
perfetto::EventContext*,
const char* name);
+
+ static std::atomic<int> session_count_;
};
} // namespace internal
diff --git a/include/perfetto/tracing/internal/track_event_interned_fields.h b/include/perfetto/tracing/internal/track_event_interned_fields.h
index cccbf0c..81bfd98 100644
--- a/include/perfetto/tracing/internal/track_event_interned_fields.h
+++ b/include/perfetto/tracing/internal/track_event_interned_fields.h
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include "perfetto/base/export.h"
#include "perfetto/tracing/track_event_interned_data_index.h"
#ifndef INCLUDE_PERFETTO_TRACING_INTERNAL_TRACK_EVENT_INTERNED_FIELDS_H_
@@ -26,7 +27,7 @@
// to share the interning buffers with Perfetto internals (e.g.
// perfetto::TracedValue implementation).
-struct InternedEventCategory
+struct PERFETTO_EXPORT InternedEventCategory
: public TrackEventInternedDataIndex<
InternedEventCategory,
perfetto::protos::pbzero::InternedData::kEventCategoriesFieldNumber,
@@ -37,14 +38,10 @@
static void Add(protos::pbzero::InternedData* interned_data,
size_t iid,
const char* value,
- size_t length) {
- auto category = interned_data->add_event_categories();
- category->set_iid(iid);
- category->set_name(value, length);
- }
+ size_t length);
};
-struct InternedEventName
+struct PERFETTO_EXPORT InternedEventName
: public TrackEventInternedDataIndex<
InternedEventName,
perfetto::protos::pbzero::InternedData::kEventNamesFieldNumber,
@@ -54,14 +51,10 @@
static void Add(protos::pbzero::InternedData* interned_data,
size_t iid,
- const char* value) {
- auto name = interned_data->add_event_names();
- name->set_iid(iid);
- name->set_name(value);
- }
+ const char* value);
};
-struct InternedDebugAnnotationName
+struct PERFETTO_EXPORT InternedDebugAnnotationName
: public TrackEventInternedDataIndex<
InternedDebugAnnotationName,
perfetto::protos::pbzero::InternedData::
@@ -72,11 +65,7 @@
static void Add(protos::pbzero::InternedData* interned_data,
size_t iid,
- const char* value) {
- auto name = interned_data->add_debug_annotation_names();
- name->set_iid(iid);
- name->set_name(value);
- }
+ const char* value);
};
} // namespace internal
diff --git a/include/perfetto/tracing/traced_value.h b/include/perfetto/tracing/traced_value.h
index ee36fb5..b1a14d3 100644
--- a/include/perfetto/tracing/traced_value.h
+++ b/include/perfetto/tracing/traced_value.h
@@ -23,13 +23,19 @@
#include "perfetto/tracing/internal/checked_scope.h"
#include "perfetto/tracing/string_helpers.h"
#include "perfetto/tracing/traced_value_forward.h"
-#include "protos/perfetto/trace/track_event/debug_annotation.pbzero.h"
+#include <memory>
#include <type_traits>
#include <utility>
namespace perfetto {
+namespace protos {
+namespace pbzero {
+class DebugAnnotation;
+}
+} // namespace protos
+
class DebugAnnotation;
// These classes provide a JSON-inspired way to write structed data into traces.
diff --git a/include/perfetto/tracing/track_event_legacy.h b/include/perfetto/tracing/track_event_legacy.h
index 175226d..cd86510 100644
--- a/include/perfetto/tracing/track_event_legacy.h
+++ b/include/perfetto/tracing/track_event_legacy.h
@@ -1163,10 +1163,18 @@
} while (0)
// Macro to efficiently determine, through polling, if a new trace has begun.
-// TODO(skyostil): Implement.
-#define TRACE_EVENT_IS_NEW_TRACE(ret) \
- do { \
- *ret = false; \
+#define TRACE_EVENT_IS_NEW_TRACE(ret) \
+ do { \
+ static int PERFETTO_UID(prev) = -1; \
+ int PERFETTO_UID(curr) = \
+ ::perfetto::internal::TrackEventInternal::GetSessionCount(); \
+ if (::PERFETTO_TRACK_EVENT_NAMESPACE::TrackEvent::IsEnabled() && \
+ (PERFETTO_UID(prev) != PERFETTO_UID(curr))) { \
+ *(ret) = true; \
+ PERFETTO_UID(prev) = PERFETTO_UID(curr); \
+ } else { \
+ *(ret) = false; \
+ } \
} while (0)
// ----------------------------------------------------------------------------
diff --git a/protos/perfetto/config/data_source_config.proto b/protos/perfetto/config/data_source_config.proto
index b49b338..22b18e3 100644
--- a/protos/perfetto/config/data_source_config.proto
+++ b/protos/perfetto/config/data_source_config.proto
@@ -41,7 +41,11 @@
message DataSourceConfig {
enum SessionInitiator {
SESSION_INITIATOR_UNSPECIFIED = 0;
- SESSION_INITIATOR_STATSD = 1;
+ // This trace was initiated from a trusted system app has DUMP and
+ // USAGE_STATS permission. This system app is expected to not expose the
+ // trace to the user of the device.
+ // This is determined by checking the UID initiating the trace.
+ SESSION_INITIATOR_TRUSTED_SYSTEM = 1;
};
// Data source unique name, e.g., "linux.ftrace". This must match
// the name passed by the data source when it registers (see
diff --git a/protos/perfetto/config/perfetto_config.proto b/protos/perfetto/config/perfetto_config.proto
index f7c2ffa..529adc7 100644
--- a/protos/perfetto/config/perfetto_config.proto
+++ b/protos/perfetto/config/perfetto_config.proto
@@ -1331,7 +1331,11 @@
message DataSourceConfig {
enum SessionInitiator {
SESSION_INITIATOR_UNSPECIFIED = 0;
- SESSION_INITIATOR_STATSD = 1;
+ // This trace was initiated from a trusted system app has DUMP and
+ // USAGE_STATS permission. This system app is expected to not expose the
+ // trace to the user of the device.
+ // This is determined by checking the UID initiating the trace.
+ SESSION_INITIATOR_TRUSTED_SYSTEM = 1;
};
// Data source unique name, e.g., "linux.ftrace". This must match
// the name passed by the data source when it registers (see
diff --git a/protos/perfetto/trace/perfetto_trace.proto b/protos/perfetto/trace/perfetto_trace.proto
index 9ea57c3..b5c30d1 100644
--- a/protos/perfetto/trace/perfetto_trace.proto
+++ b/protos/perfetto/trace/perfetto_trace.proto
@@ -1331,7 +1331,11 @@
message DataSourceConfig {
enum SessionInitiator {
SESSION_INITIATOR_UNSPECIFIED = 0;
- SESSION_INITIATOR_STATSD = 1;
+ // This trace was initiated from a trusted system app has DUMP and
+ // USAGE_STATS permission. This system app is expected to not expose the
+ // trace to the user of the device.
+ // This is determined by checking the UID initiating the trace.
+ SESSION_INITIATOR_TRUSTED_SYSTEM = 1;
};
// Data source unique name, e.g., "linux.ftrace". This must match
// the name passed by the data source when it registers (see
diff --git a/src/base/periodic_task.cc b/src/base/periodic_task.cc
index deeed9a..fcbc9de 100644
--- a/src/base/periodic_task.cc
+++ b/src/base/periodic_task.cc
@@ -118,6 +118,9 @@
return; // Destroyed or Reset() in the meanwhile.
PERFETTO_DCHECK_THREAD(thiz->thread_checker_);
if (thiz->timer_fd_) {
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+ PERFETTO_FATAL("timerfd for periodic tasks unsupported on Windows");
+#else
// If we are using a timerfd there is no need to repeatedly call
// PostDelayedTask(). The kernel will wakeup the timer fd periodically. We
// just need to read() it.
@@ -130,6 +133,7 @@
PERFETTO_PLOG("read(timerfd) failed, falling back on PostDelayedTask");
thiz->ResetTimerFd();
}
+#endif
}
// The repetition of the if() is to deal with the ResetTimerFd() case above.
if (!thiz->timer_fd_) {
diff --git a/src/base/periodic_task_unittest.cc b/src/base/periodic_task_unittest.cc
index 11bafda..4919720 100644
--- a/src/base/periodic_task_unittest.cc
+++ b/src/base/periodic_task_unittest.cc
@@ -90,7 +90,8 @@
dup2(*dev_null, pt.timer_fd_for_testing());
}
#else
- EXPECT_EQ(pt.timer_fd_for_testing(), base::ScopedPlatformHandle::kInvalid);
+ EXPECT_FALSE(base::ScopedPlatformHandle::ValidityChecker::IsValid(
+ pt.timer_fd_for_testing()));
#endif
if (num_callbacks == 6)
quit_closure();
diff --git a/src/profiling/common/producer_support.cc b/src/profiling/common/producer_support.cc
index 2ed2852..2347edd 100644
--- a/src/profiling/common/producer_support.cc
+++ b/src/profiling/common/producer_support.cc
@@ -100,7 +100,7 @@
switch (ds_config.session_initiator()) {
case DataSourceConfig::SESSION_INITIATOR_UNSPECIFIED:
return pkg.profileable_from_shell || pkg.debuggable;
- case DataSourceConfig::SESSION_INITIATOR_STATSD:
+ case DataSourceConfig::SESSION_INITIATOR_TRUSTED_SYSTEM:
return pkg.profileable || pkg.debuggable;
}
}
diff --git a/src/profiling/common/producer_support_unittest.cc b/src/profiling/common/producer_support_unittest.cc
index c1131c9..33fdf28 100644
--- a/src/profiling/common/producer_support_unittest.cc
+++ b/src/profiling/common/producer_support_unittest.cc
@@ -106,7 +106,8 @@
TEST(CanProfileAndroidTest, UserProfileableMatchingInstallerStatsd) {
DataSourceConfig ds_config;
- ds_config.set_session_initiator(DataSourceConfig::SESSION_INITIATOR_STATSD);
+ ds_config.set_session_initiator(
+ DataSourceConfig::SESSION_INITIATOR_TRUSTED_SYSTEM);
auto tmp = base::TempFile::Create();
constexpr char content[] =
"invalid.example 10001 0 /data/user/0/invalid.example "
@@ -131,7 +132,8 @@
TEST(CanProfileAndroidTest, UserProfileableNonMatchingInstallerStatsd) {
DataSourceConfig ds_config;
- ds_config.set_session_initiator(DataSourceConfig::SESSION_INITIATOR_STATSD);
+ ds_config.set_session_initiator(
+ DataSourceConfig::SESSION_INITIATOR_TRUSTED_SYSTEM);
auto tmp = base::TempFile::Create();
constexpr char content[] =
"invalid.example 10001 0 /data/user/0/invalid.example "
diff --git a/src/profiling/common/unwind_support.cc b/src/profiling/common/unwind_support.cc
index 205ede9..5302015 100644
--- a/src/profiling/common/unwind_support.cc
+++ b/src/profiling/common/unwind_support.cc
@@ -67,6 +67,7 @@
if (!base::ReadFileDescriptor(*fd_, &content))
return false;
+ unwindstack::SharedString name("");
unwindstack::MapInfo* prev_map = nullptr;
unwindstack::MapInfo* prev_real_map = nullptr;
return android::procinfo::ReadMapFileContent(
@@ -77,9 +78,13 @@
strncmp(mapinfo.name.c_str() + 5, "ashmem/", 7) != 0) {
flags |= unwindstack::MAPS_FLAGS_DEVICE_MAP;
}
+ // Share the string if it matches for consecutive maps.
+ if (name != mapinfo.name) {
+ name = unwindstack::SharedString(mapinfo.name);
+ }
maps_.emplace_back(new unwindstack::MapInfo(
prev_map, prev_real_map, mapinfo.start, mapinfo.end, mapinfo.pgoff,
- flags, mapinfo.name));
+ flags, name));
prev_map = maps_.back().get();
if (!prev_map->IsBlank()) {
prev_real_map = prev_map;
diff --git a/src/profiling/deobfuscator.cc b/src/profiling/deobfuscator.cc
index 2a7b9c0..ba58dfa 100644
--- a/src/profiling/deobfuscator.cc
+++ b/src/profiling/deobfuscator.cc
@@ -16,6 +16,7 @@
#include "src/profiling/deobfuscator.h"
+#include "perfetto/base/status.h"
#include "perfetto/ext/base/file_utils.h"
#include "perfetto/ext/base/scoped_file.h"
#include "perfetto/ext/base/string_splitter.h"
@@ -157,37 +158,36 @@
// See https://www.guardsquare.com/en/products/proguard/manual/retrace for the
// file format we are parsing.
-bool ProguardParser::AddLine(std::string line) {
- if (line.length() == 0)
- return true;
+base::Status ProguardParser::AddLine(std::string line) {
+ if (line.length() == 0 || line[0] == '#')
+ return base::Status();
bool is_member = line[0] == ' ';
if (is_member && !current_class_) {
- PERFETTO_ELOG("Failed to parse proguard map. Saw member before class.");
- return false;
+ return base::Status(
+ "Failed to parse proguard map. Saw member before class.");
}
if (!is_member) {
auto opt_cls = ParseClass(std::move(line));
if (!opt_cls)
- return false;
+ return base::Status("Class not found.");
auto p = mapping_.emplace(std::move(opt_cls->obfuscated_name),
std::move(opt_cls->deobfuscated_name));
if (!p.second) {
- PERFETTO_ELOG("Duplicate class.");
- return false;
+ return base::Status("Duplicate class.");
}
current_class_ = &p.first->second;
} else {
auto opt_member = ParseMember(std::move(line));
if (!opt_member)
- return false;
+ return base::Status("Failed to parse member.");
switch (opt_member->type) {
case (ProguardMemberType::kField): {
if (!current_class_->AddField(opt_member->obfuscated_name,
opt_member->deobfuscated_name)) {
- PERFETTO_ELOG("Member redefinition: %s.%s. Proguard map invalid",
- current_class_->deobfuscated_name().c_str(),
- opt_member->deobfuscated_name.c_str());
- return false;
+ return base::Status(std::string("Member redefinition: ") +
+ current_class_->deobfuscated_name().c_str() +
+ "." + opt_member->deobfuscated_name.c_str() +
+ " Proguard map invalid");
}
break;
}
@@ -198,13 +198,19 @@
}
}
}
- return true;
+ return base::Status();
}
bool ProguardParser::AddLines(std::string contents) {
+ size_t lineno = 1;
for (base::StringSplitter lines(std::move(contents), '\n'); lines.Next();) {
- if (!AddLine(lines.cur_token()))
+ auto status = AddLine(lines.cur_token());
+ if (!status.ok()) {
+ PERFETTO_ELOG("Failed to parse proguard map (line %zu): %s", lineno,
+ status.c_message());
return false;
+ }
+ lineno++;
}
return true;
}
diff --git a/src/profiling/deobfuscator.h b/src/profiling/deobfuscator.h
index 061b5da..b96f292 100644
--- a/src/profiling/deobfuscator.h
+++ b/src/profiling/deobfuscator.h
@@ -22,6 +22,7 @@
#include <string>
#include <utility>
#include <vector>
+#include "perfetto/base/status.h"
namespace perfetto {
namespace profiling {
@@ -59,7 +60,7 @@
bool AddField(std::string obfuscated_name, std::string deobfuscated_name) {
auto p = deobfuscated_fields_.emplace(std::move(obfuscated_name),
deobfuscated_name);
- return p.second && p.first->second == deobfuscated_name;
+ return p.second || p.first->second == deobfuscated_name;
}
void AddMethod(std::string obfuscated_name, std::string deobfuscated_name) {
@@ -90,7 +91,7 @@
public:
// A return value of false means this line failed to parse. This leaves the
// parser in an undefined state and it should no longer be used.
- bool AddLine(std::string line);
+ base::Status AddLine(std::string line);
bool AddLines(std::string contents);
std::map<std::string, ObfuscatedClass> ConsumeMapping() {
diff --git a/src/profiling/deobfuscator_unittest.cc b/src/profiling/deobfuscator_unittest.cc
index 66001dd..02a0e8d 100644
--- a/src/profiling/deobfuscator_unittest.cc
+++ b/src/profiling/deobfuscator_unittest.cc
@@ -37,8 +37,10 @@
TEST(ProguardParserTest, ReadClass) {
ProguardParser p;
- ASSERT_TRUE(p.AddLine(
- "android.arch.core.executor.ArchTaskExecutor -> android.arch.a.a.a:"));
+ ASSERT_TRUE(
+ p.AddLine(
+ "android.arch.core.executor.ArchTaskExecutor -> android.arch.a.a.a:")
+ .ok());
ASSERT_THAT(p.ConsumeMapping(),
ElementsAre(std::pair<std::string, ObfuscatedClass>(
"android.arch.a.a.a",
@@ -47,22 +49,28 @@
TEST(ProguardParserTest, MissingColon) {
ProguardParser p;
- ASSERT_FALSE(p.AddLine(
- "android.arch.core.executor.ArchTaskExecutor -> android.arch.a.a.a"));
+ ASSERT_FALSE(
+ p.AddLine(
+ "android.arch.core.executor.ArchTaskExecutor -> android.arch.a.a.a")
+ .ok());
}
TEST(ProguardParserTest, UnexpectedMember) {
ProguardParser p;
ASSERT_FALSE(
- p.AddLine(" android.arch.core.executor.TaskExecutor mDelegate -> b"));
+ p.AddLine(" android.arch.core.executor.TaskExecutor mDelegate -> b")
+ .ok());
}
TEST(ProguardParserTest, Member) {
ProguardParser p;
- ASSERT_TRUE(p.AddLine(
- "android.arch.core.executor.ArchTaskExecutor -> android.arch.a.a.a:"));
ASSERT_TRUE(
- p.AddLine(" android.arch.core.executor.TaskExecutor mDelegate -> b"));
+ p.AddLine(
+ "android.arch.core.executor.ArchTaskExecutor -> android.arch.a.a.a:")
+ .ok());
+ ASSERT_TRUE(
+ p.AddLine(" android.arch.core.executor.TaskExecutor mDelegate -> b")
+ .ok());
std::map<std::string, std::string> deobfuscated_fields{{"b", "mDelegate"}};
ASSERT_THAT(
p.ConsumeMapping(),
@@ -74,9 +82,11 @@
TEST(ProguardParserTest, Method) {
ProguardParser p;
- ASSERT_TRUE(p.AddLine(
- "android.arch.core.executor.ArchTaskExecutor -> android.arch.a.a.a:"));
- ASSERT_TRUE(p.AddLine(" 15:15:boolean isMainThread():116:116 -> b"));
+ ASSERT_TRUE(
+ p.AddLine(
+ "android.arch.core.executor.ArchTaskExecutor -> android.arch.a.a.a:")
+ .ok());
+ ASSERT_TRUE(p.AddLine(" 15:15:boolean isMainThread():116:116 -> b").ok());
auto mapping = p.ConsumeMapping();
ASSERT_THAT(mapping, ElementsAre(Pair("android.arch.a.a.a", _)));
EXPECT_THAT(
@@ -87,11 +97,13 @@
TEST(ProguardParserTest, AmbiguousMethodSameCls) {
ProguardParser p;
- ASSERT_TRUE(p.AddLine(
- "android.arch.core.executor.ArchTaskExecutor -> android.arch.a.a.a:"));
- ASSERT_TRUE(p.AddLine(" 15:15:boolean isMainThread():116:116 -> b"));
ASSERT_TRUE(
- p.AddLine(" 15:15:boolean somethingDifferent(int):116:116 -> b"));
+ p.AddLine(
+ "android.arch.core.executor.ArchTaskExecutor -> android.arch.a.a.a:")
+ .ok());
+ ASSERT_TRUE(p.AddLine(" 15:15:boolean isMainThread():116:116 -> b").ok());
+ ASSERT_TRUE(
+ p.AddLine(" 15:15:boolean somethingDifferent(int):116:116 -> b").ok());
auto mapping = p.ConsumeMapping();
ASSERT_THAT(mapping, ElementsAre(Pair("android.arch.a.a.a", _)));
EXPECT_THAT(
@@ -102,11 +114,14 @@
TEST(ProguardParserTest, AmbiguousMethodDifferentCls) {
ProguardParser p;
- ASSERT_TRUE(p.AddLine(
- "android.arch.core.executor.ArchTaskExecutor -> android.arch.a.a.a:"));
- ASSERT_TRUE(p.AddLine(" 15:15:boolean isMainThread():116:116 -> b"));
ASSERT_TRUE(
- p.AddLine(" 15:15:boolean Foo.somethingDifferent(int):116:116 -> b"));
+ p.AddLine(
+ "android.arch.core.executor.ArchTaskExecutor -> android.arch.a.a.a:")
+ .ok());
+ ASSERT_TRUE(p.AddLine(" 15:15:boolean isMainThread():116:116 -> b").ok());
+ ASSERT_TRUE(
+ p.AddLine(" 15:15:boolean Foo.somethingDifferent(int):116:116 -> b")
+ .ok());
auto mapping = p.ConsumeMapping();
ASSERT_THAT(mapping, ElementsAre(Pair("android.arch.a.a.a", _)));
EXPECT_THAT(mapping.find("android.arch.a.a.a")->second.deobfuscated_methods(),
@@ -118,12 +133,15 @@
TEST(ProguardParserTest, AmbiguousMethodSameAndDifferentCls) {
ProguardParser p;
- ASSERT_TRUE(p.AddLine(
- "android.arch.core.executor.ArchTaskExecutor -> android.arch.a.a.a:"));
- ASSERT_TRUE(p.AddLine(" 15:15:boolean isMainThread():116:116 -> b"));
- ASSERT_TRUE(p.AddLine(" 15:15:boolean what(String):116:116 -> b"));
ASSERT_TRUE(
- p.AddLine(" 15:15:boolean Foo.somethingDifferent(int):116:116 -> b"));
+ p.AddLine(
+ "android.arch.core.executor.ArchTaskExecutor -> android.arch.a.a.a:")
+ .ok());
+ ASSERT_TRUE(p.AddLine(" 15:15:boolean isMainThread():116:116 -> b").ok());
+ ASSERT_TRUE(p.AddLine(" 15:15:boolean what(String):116:116 -> b").ok());
+ ASSERT_TRUE(
+ p.AddLine(" 15:15:boolean Foo.somethingDifferent(int):116:116 -> b")
+ .ok());
auto mapping = p.ConsumeMapping();
ASSERT_THAT(mapping, ElementsAre(Pair("android.arch.a.a.a", _)));
EXPECT_THAT(mapping.find("android.arch.a.a.a")->second.deobfuscated_methods(),
@@ -135,13 +153,17 @@
TEST(ProguardParserTest, AmbiguousMethodSameAndDifferentCls2) {
ProguardParser p;
- ASSERT_TRUE(p.AddLine(
- "android.arch.core.executor.ArchTaskExecutor -> android.arch.a.a.a:"));
- ASSERT_TRUE(p.AddLine(" 15:15:boolean isMainThread():116:116 -> b"));
- ASSERT_TRUE(p.AddLine(" 15:15:boolean what(String):116:116 -> b"));
ASSERT_TRUE(
- p.AddLine(" 15:15:boolean Foo.somethingDifferent(int):116:116 -> b"));
- ASSERT_TRUE(p.AddLine(" 15:15:boolean Foo.third(int,int):116:116 -> b"));
+ p.AddLine(
+ "android.arch.core.executor.ArchTaskExecutor -> android.arch.a.a.a:")
+ .ok());
+ ASSERT_TRUE(p.AddLine(" 15:15:boolean isMainThread():116:116 -> b").ok());
+ ASSERT_TRUE(p.AddLine(" 15:15:boolean what(String):116:116 -> b").ok());
+ ASSERT_TRUE(
+ p.AddLine(" 15:15:boolean Foo.somethingDifferent(int):116:116 -> b")
+ .ok());
+ ASSERT_TRUE(
+ p.AddLine(" 15:15:boolean Foo.third(int,int):116:116 -> b").ok());
auto mapping = p.ConsumeMapping();
ASSERT_THAT(mapping, ElementsAre(Pair("android.arch.a.a.a", _)));
EXPECT_THAT(mapping.find("android.arch.a.a.a")->second.deobfuscated_methods(),
@@ -153,28 +175,53 @@
TEST(ProguardParserTest, DuplicateClass) {
ProguardParser p;
- ASSERT_TRUE(p.AddLine(
- "android.arch.core.executor.ArchTaskExecutor -> android.arch.a.a.a:"));
- ASSERT_FALSE(p.AddLine(
- "android.arch.core.executor.ArchTaskExecutor2 -> android.arch.a.a.a:"));
+ ASSERT_TRUE(
+ p.AddLine(
+ "android.arch.core.executor.ArchTaskExecutor -> android.arch.a.a.a:")
+ .ok());
+ ASSERT_FALSE(p.AddLine("android.arch.core.executor.ArchTaskExecutor2 -> "
+ "android.arch.a.a.a:")
+ .ok());
}
TEST(ProguardParserTest, DuplicateField) {
ProguardParser p;
- ASSERT_TRUE(p.AddLine(
- "android.arch.core.executor.ArchTaskExecutor -> android.arch.a.a.a:"));
ASSERT_TRUE(
- p.AddLine(" android.arch.core.executor.TaskExecutor mDelegate -> b"));
+ p.AddLine(
+ "android.arch.core.executor.ArchTaskExecutor -> android.arch.a.a.a:")
+ .ok());
+ ASSERT_TRUE(
+ p.AddLine(" android.arch.core.executor.TaskExecutor mDelegate -> b")
+ .ok());
ASSERT_FALSE(
- p.AddLine(" android.arch.core.executor.TaskExecutor mDelegate2 -> b"));
+ p.AddLine(" android.arch.core.executor.TaskExecutor mDelegate2 -> b")
+ .ok());
}
TEST(ProguardParserTest, DuplicateMethod) {
ProguardParser p;
- ASSERT_TRUE(p.AddLine(
- "android.arch.core.executor.ArchTaskExecutor -> android.arch.a.a.a:"));
- ASSERT_TRUE(p.AddLine(" 15:15:boolean isMainThread():116:116 -> b"));
- ASSERT_TRUE(p.AddLine(" 15:15:boolean doSomething(boolean):116:116 -> b"));
+ ASSERT_TRUE(
+ p.AddLine(
+ "android.arch.core.executor.ArchTaskExecutor -> android.arch.a.a.a:")
+ .ok());
+ ASSERT_TRUE(p.AddLine(" 15:15:boolean isMainThread():116:116 -> b").ok());
+ ASSERT_TRUE(
+ p.AddLine(" 15:15:boolean doSomething(boolean):116:116 -> b").ok());
+}
+
+TEST(ProguardParserTest, DuplicateFieldSame) {
+ ProguardParser p;
+ ASSERT_TRUE(
+ p.AddLine(
+ "android.arch.core.executor.ArchTaskExecutor -> android.arch.a.a.a:")
+ .ok());
+ ASSERT_TRUE(
+ p.AddLine(" android.arch.core.executor.TaskExecutor mDelegate -> b")
+ .ok());
+ ASSERT_TRUE(
+ p.AddLine(
+ " 1:1:android.arch.core.executor.TaskExecutor mDelegate -> b")
+ .ok());
}
} // namespace
diff --git a/src/profiling/memory/heapprofd_producer.cc b/src/profiling/memory/heapprofd_producer.cc
index 1e434ee..16da1fa 100644
--- a/src/profiling/memory/heapprofd_producer.cc
+++ b/src/profiling/memory/heapprofd_producer.cc
@@ -375,7 +375,7 @@
void HeapprofdProducer::SetupDataSource(DataSourceInstanceID id,
const DataSourceConfig& ds_config) {
if (ds_config.session_initiator() ==
- DataSourceConfig::SESSION_INITIATOR_STATSD) {
+ DataSourceConfig::SESSION_INITIATOR_TRUSTED_SYSTEM) {
PERFETTO_LOG("Setting up datasource: statsd initiator.");
} else {
PERFETTO_LOG("Setting up datasource: non-statsd initiator.");
diff --git a/src/trace_processor/importers/systrace/systrace_parser.h b/src/trace_processor/importers/systrace/systrace_parser.h
index f069019..2531150 100644
--- a/src/trace_processor/importers/systrace/systrace_parser.h
+++ b/src/trace_processor/importers/systrace/systrace_parser.h
@@ -155,8 +155,10 @@
size_t name_index = 2 + tgid_length + 1;
out->name = base::StringView(
s + name_index, len - name_index - (s[len - 1] == '\n' ? 1 : 0));
- if (out->name.empty())
- return SystraceParseResult::kFailure;
+ if (out->name.empty()) {
+ static const char kEmptySliceName[] = "[empty slice name]";
+ out->name = base::StringView(kEmptySliceName);
+ }
return SystraceParseResult::kSuccess;
}
case 'E': {
diff --git a/src/trace_processor/importers/systrace/systrace_parser_unittest.cc b/src/trace_processor/importers/systrace/systrace_parser_unittest.cc
index 5c65514..b0e9de1 100644
--- a/src/trace_processor/importers/systrace/systrace_parser_unittest.cc
+++ b/src/trace_processor/importers/systrace/systrace_parser_unittest.cc
@@ -41,7 +41,9 @@
ASSERT_EQ(ParseSystraceTracePoint("C", &result), Result::kFailure);
ASSERT_EQ(ParseSystraceTracePoint("S", &result), Result::kFailure);
ASSERT_EQ(ParseSystraceTracePoint("F", &result), Result::kFailure);
- ASSERT_EQ(ParseSystraceTracePoint("B|42|\n", &result), Result::kFailure);
+
+ ASSERT_EQ(ParseSystraceTracePoint("B|42|\n", &result), Result::kSuccess);
+ EXPECT_EQ(result, SystraceTracePoint::B(42, "[empty slice name]"));
ASSERT_EQ(ParseSystraceTracePoint("B|1|foo", &result), Result::kSuccess);
EXPECT_EQ(result, SystraceTracePoint::B(1, "foo"));
diff --git a/src/trace_processor/metrics/android/android_startup.sql b/src/trace_processor/metrics/android/android_startup.sql
index 6ea79ee..0c6d429 100644
--- a/src/trace_processor/metrics/android/android_startup.sql
+++ b/src/trace_processor/metrics/android/android_startup.sql
@@ -229,10 +229,41 @@
CREATE VIRTUAL TABLE gc_slices_by_state
USING SPAN_JOIN(gc_slices PARTITIONED utid, thread_state_extended PARTITIONED utid);
+DROP TABLE IF EXISTS gc_slices_by_state_materialized;
+CREATE TABLE gc_slices_by_state_materialized AS
+SELECT launch_id, SUM(dur) as sum_dur
+FROM gc_slices_by_state
+WHERE state = 'Running'
+GROUP BY launch_id;
+
DROP TABLE IF EXISTS launch_threads_cpu;
CREATE VIRTUAL TABLE launch_threads_cpu
USING SPAN_JOIN(launch_threads PARTITIONED utid, thread_state_extended PARTITIONED utid);
+DROP TABLE IF EXISTS launch_threads_cpu_materialized;
+CREATE TABLE launch_threads_cpu_materialized AS
+SELECT launch_id, SUM(dur) as sum_dur
+FROM launch_threads_cpu
+WHERE thread_name = 'Jit thread pool' AND state = 'Running'
+GROUP BY launch_id;
+
+DROP TABLE IF EXISTS activity_names_materialized;
+CREATE TABLE activity_names_materialized AS
+SELECT launch_id, slice_name, slice_ts
+FROM main_process_slice_unaggregated
+WHERE (slice_name LIKE 'performResume:%' OR slice_name LIKE 'performCreate:%');
+
+DROP TABLE IF EXISTS jit_compiled_methods_materialized;
+CREATE TABLE jit_compiled_methods_materialized AS
+SELECT
+ launch_id,
+ COUNT(1) as count
+FROM main_process_slice_unaggregated
+WHERE
+ slice_name LIKE 'JIT compiling%'
+ AND thread_name = 'Jit thread pool'
+GROUP BY launch_id;
+
DROP VIEW IF EXISTS startup_view;
CREATE VIEW startup_view AS
SELECT
@@ -240,20 +271,18 @@
'startup_id', launches.id,
'package_name', launches.package,
'process_name', (
- SELECT name FROM process
- WHERE upid IN (
- SELECT upid FROM launch_processes p
- WHERE p.launch_id = launches.id
- LIMIT 1
- )
+ SELECT p.name
+ FROM launch_processes lp
+ JOIN process p USING (upid)
+ WHERE lp.launch_id = launches.id
+ LIMIT 1
),
'process', (
- SELECT metadata FROM process_metadata
- WHERE upid IN (
- SELECT upid FROM launch_processes p
- WHERE p.launch_id = launches.id
- LIMIT 1
- )
+ SELECT m.metadata
+ FROM process_metadata m
+ JOIN launch_processes p USING (upid)
+ WHERE p.launch_id = launches.id
+ LIMIT 1
),
'activities', (
SELECT RepeatedField(AndroidStartupMetric_Activity(
@@ -261,9 +290,8 @@
'method', (SELECT STR_SPLIT(s.slice_name, ':', 0)),
'ts_method_start', s.slice_ts
))
- FROM main_process_slice_unaggregated s
+ FROM activity_names_materialized s
WHERE s.launch_id = launches.id
- AND (slice_name LIKE 'performResume:%' OR slice_name LIKE 'performCreate:%')
),
'zygote_new_process', EXISTS(SELECT TRUE FROM zygote_forks_by_id WHERE id = launches.id),
'activity_hosting_process_count', (
@@ -422,21 +450,18 @@
WHERE s.launch_id = launches.id AND name = 'VerifyClass'
),
'jit_compiled_methods', (
- SELECT SUM(1)
- FROM main_process_slice_unaggregated
- WHERE slice_name LIKE 'JIT compiling%'
- AND thread_name = 'Jit thread pool'
+ SELECT count
+ FROM jit_compiled_methods_materialized s
+ WHERE s.launch_id = launches.id
),
'time_jit_thread_pool_on_cpu', (
SELECT
- NULL_IF_EMPTY(AndroidStartupMetric_Slice(
- 'dur_ns', SUM(dur),
- 'dur_ms', SUM(dur) / 1e6))
- FROM launch_threads_cpu
- WHERE
- launch_id = launches.id
- AND thread_name = 'Jit thread pool'
- AND state = 'Running'
+ NULL_IF_EMPTY(AndroidStartupMetric_Slice(
+ 'dur_ns', sum_dur,
+ 'dur_ms', sum_dur / 1e6
+ ))
+ FROM launch_threads_cpu_materialized
+ WHERE launch_id = launches.id
),
'time_gc_total', (
SELECT slice_proto
@@ -446,11 +471,11 @@
'time_gc_on_cpu', (
SELECT
NULL_IF_EMPTY(AndroidStartupMetric_Slice(
- 'dur_ns', SUM(dur),
- 'dur_ms', SUM(dur) / 1e6
+ 'dur_ns', sum_dur,
+ 'dur_ms', sum_dur / 1e6
))
- FROM gc_slices_by_state
- WHERE launch_id = launches.id AND state = 'Running'
+ FROM gc_slices_by_state_materialized
+ WHERE launch_id = launches.id
)
),
'hsc', (
diff --git a/src/tracing/console_interceptor.cc b/src/tracing/console_interceptor.cc
index c3b0e1c..99ce956 100644
--- a/src/tracing/console_interceptor.cc
+++ b/src/tracing/console_interceptor.cc
@@ -317,7 +317,7 @@
va_start(args, format);
written = vsnprintf(&tls.message_buffer[tls.buffer_pos],
static_cast<size_t>(remaining), format, args);
- PERFETTO_DCHECK(written > 0);
+ PERFETTO_DCHECK(written >= 0);
va_end(args);
}
diff --git a/src/tracing/core/tracing_service_impl.cc b/src/tracing/core/tracing_service_impl.cc
index 77f83d0..ea17dd4 100644
--- a/src/tracing/core/tracing_service_impl.cc
+++ b/src/tracing/core/tracing_service_impl.cc
@@ -441,7 +441,8 @@
std::unique_ptr<TracingService::ConsumerEndpoint>
TracingServiceImpl::ConnectConsumer(Consumer* consumer, uid_t uid) {
PERFETTO_DCHECK_THREAD(thread_checker_);
- PERFETTO_DLOG("Consumer %p connected", reinterpret_cast<void*>(consumer));
+ PERFETTO_DLOG("Consumer %p connected from UID %" PRIu64,
+ reinterpret_cast<void*>(consumer), static_cast<uint64_t>(uid));
std::unique_ptr<ConsumerEndpointImpl> endpoint(
new ConsumerEndpointImpl(this, task_runner_, consumer, uid));
auto it_and_inserted = consumers_.emplace(endpoint.get());
@@ -2401,8 +2402,21 @@
ds_config.set_stop_timeout_ms(tracing_session->data_source_stop_timeout_ms());
ds_config.set_enable_extra_guardrails(
tracing_session->config.enable_extra_guardrails());
- if (tracing_session->consumer_uid == 1066 /* AID_STATSD */) {
- ds_config.set_session_initiator(DataSourceConfig::SESSION_INITIATOR_STATSD);
+ if (tracing_session->consumer_uid == 1066 /* AID_STATSD */ &&
+ tracing_session->config.statsd_metadata().triggering_config_uid() !=
+ 2000 /* AID_SHELL */
+ && tracing_session->config.statsd_metadata().triggering_config_uid() !=
+ 0 /* AID_ROOT */) {
+ // StatsD can be triggered either by shell, root or an app that has DUMP and
+ // USAGE_STATS permission. When triggered by shell or root, we do not want
+ // to consider the trace a trusted system trace, as it was initiated by the
+ // user. Otherwise, it has to come from an app with DUMP and
+ // PACKAGE_USAGE_STATS, which has to be preinstalled and trusted by the
+ // system.
+ // Check for shell / root: https://bit.ly/3b7oZNi
+ // Check for DUMP or PACKAGE_USAGE_STATS: https://bit.ly/3ep0NrR
+ ds_config.set_session_initiator(
+ DataSourceConfig::SESSION_INITIATOR_TRUSTED_SYSTEM);
} else {
// Unset in case the consumer set it.
// We need to be able to trust this field.
diff --git a/src/tracing/internal/tracing_muxer_impl.cc b/src/tracing/internal/tracing_muxer_impl.cc
index 280a990..1fb5ae8 100644
--- a/src/tracing/internal/tracing_muxer_impl.cc
+++ b/src/tracing/internal/tracing_muxer_impl.cc
@@ -58,6 +58,58 @@
namespace {
+// A task runner which prevents calls to DataSource::Trace() while an operation
+// is in progress. Used to guard against unexpected re-entrancy where the
+// user-provided task runner implementation tries to enter a trace point under
+// the hood.
+class NonReentrantTaskRunner : public base::TaskRunner {
+ public:
+ NonReentrantTaskRunner(TracingMuxer* muxer,
+ std::unique_ptr<base::TaskRunner> task_runner)
+ : muxer_(muxer), task_runner_(std::move(task_runner)) {}
+
+ // base::TaskRunner implementation.
+ void PostTask(std::function<void()> task) override {
+ CallWithGuard([&] { task_runner_->PostTask(std::move(task)); });
+ }
+
+ void PostDelayedTask(std::function<void()> task, uint32_t delay_ms) override {
+ CallWithGuard(
+ [&] { task_runner_->PostDelayedTask(std::move(task), delay_ms); });
+ }
+
+ void AddFileDescriptorWatch(base::PlatformHandle fd,
+ std::function<void()> callback) override {
+ CallWithGuard(
+ [&] { task_runner_->AddFileDescriptorWatch(fd, std::move(callback)); });
+ }
+
+ void RemoveFileDescriptorWatch(base::PlatformHandle fd) override {
+ CallWithGuard([&] { task_runner_->RemoveFileDescriptorWatch(fd); });
+ }
+
+ bool RunsTasksOnCurrentThread() const override {
+ bool result;
+ CallWithGuard([&] { result = task_runner_->RunsTasksOnCurrentThread(); });
+ return result;
+ }
+
+ private:
+ template <typename T>
+ void CallWithGuard(T lambda) const {
+ auto* root_tls = muxer_->GetOrCreateTracingTLS();
+ if (PERFETTO_UNLIKELY(root_tls->is_in_trace_point)) {
+ lambda();
+ return;
+ }
+ ScopedReentrancyAnnotator scoped_annotator(*root_tls);
+ lambda();
+ }
+
+ TracingMuxer* const muxer_;
+ std::unique_ptr<base::TaskRunner> task_runner_;
+};
+
class StopArgsImpl : public DataSourceBase::StopArgs {
public:
std::function<void()> HandleStopAsynchronously() const override {
@@ -642,9 +694,11 @@
: TracingMuxer(args.platform ? args.platform
: Platform::GetDefaultPlatform()) {
PERFETTO_DETACH_FROM_THREAD(thread_checker_);
+ instance_ = this;
// Create the thread where muxer, producers and service will live.
- task_runner_ = platform_->CreateTaskRunner({});
+ task_runner_.reset(
+ new NonReentrantTaskRunner(this, platform_->CreateTaskRunner({})));
// Run the initializer on that thread.
task_runner_->PostTask([this, args] { Initialize(args); });
@@ -1561,7 +1615,7 @@
void TracingMuxerImpl::InitializeInstance(const TracingInitArgs& args) {
if (instance_ != TracingMuxerFake::Get())
PERFETTO_FATAL("Tracing already initialized");
- instance_ = new TracingMuxerImpl(args);
+ new TracingMuxerImpl(args);
}
TracingMuxer::~TracingMuxer() = default;
diff --git a/src/tracing/internal/track_event_internal.cc b/src/tracing/internal/track_event_internal.cc
index cffab69..736c312 100644
--- a/src/tracing/internal/track_event_internal.cc
+++ b/src/tracing/internal/track_event_internal.cc
@@ -95,6 +95,9 @@
const Track TrackEventInternal::kDefaultTrack{};
// static
+std::atomic<int> TrackEventInternal::session_count_{};
+
+// static
bool TrackEventInternal::Initialize(
const TrackEventCategoryRegistry& registry,
bool (*register_data_source)(const DataSourceDescriptor&)) {
@@ -172,6 +175,7 @@
// static
void TrackEventInternal::OnStart(const DataSourceBase::StartArgs& args) {
+ session_count_.fetch_add(1);
ForEachObserver([&](TrackEventSessionObserver*& o) {
if (o)
o->OnStart(args);
@@ -219,6 +223,14 @@
}
break;
}
+ // No match? Must be a dynamic category.
+ DynamicCategory dyn_category(std::string(member_name, name_size));
+ Category ref_category{Category::FromDynamicCategory(dyn_category)};
+ if (IsCategoryEnabled(registry, config, ref_category)) {
+ result = true;
+ // Break ForEachGroupMember() loop.
+ return false;
+ }
// No match found => keep iterating.
return true;
});
@@ -291,6 +303,11 @@
}
// static
+int TrackEventInternal::GetSessionCount() {
+ return session_count_.load();
+}
+
+// static
void TrackEventInternal::ResetIncrementalState(TraceWriterBase* trace_writer,
uint64_t timestamp) {
auto default_track = ThreadTrack::Current();
diff --git a/src/tracing/internal/track_event_interned_fields.cc b/src/tracing/internal/track_event_interned_fields.cc
index a9c5f0e..804aeb4 100644
--- a/src/tracing/internal/track_event_interned_fields.cc
+++ b/src/tracing/internal/track_event_interned_fields.cc
@@ -21,9 +21,38 @@
InternedEventCategory::~InternedEventCategory() = default;
+// static
+void InternedEventCategory::Add(protos::pbzero::InternedData* interned_data,
+ size_t iid,
+ const char* value,
+ size_t length) {
+ auto category = interned_data->add_event_categories();
+ category->set_iid(iid);
+ category->set_name(value, length);
+}
+
InternedEventName::~InternedEventName() = default;
+// static
+void InternedEventName::Add(protos::pbzero::InternedData* interned_data,
+ size_t iid,
+ const char* value) {
+ auto name = interned_data->add_event_names();
+ name->set_iid(iid);
+ name->set_name(value);
+}
+
InternedDebugAnnotationName::~InternedDebugAnnotationName() = default;
+// static
+void InternedDebugAnnotationName::Add(
+ protos::pbzero::InternedData* interned_data,
+ size_t iid,
+ const char* value) {
+ auto name = interned_data->add_debug_annotation_names();
+ name->set_iid(iid);
+ name->set_name(value);
+}
+
} // namespace internal
} // namespace perfetto
diff --git a/src/tracing/test/api_integrationtest.cc b/src/tracing/test/api_integrationtest.cc
index 84c0d7c..061e2fe 100644
--- a/src/tracing/test/api_integrationtest.cc
+++ b/src/tracing/test/api_integrationtest.cc
@@ -2457,7 +2457,8 @@
slices,
ElementsAre("B:foo.FooEvent", "B:bar.BarEvent", "B:foo,bar.MultiFooBar",
"B:baz,bar,quux.MultiBar", "B:red,green,blue,foo.MultiFoo",
- "B:test.TagEvent", "B:$dynamic,$foo.DynamicGroupFooEvent",
+ "B:red,green,blue,yellow.MultiNone", "B:test.TagEvent",
+ "B:$dynamic,$foo.DynamicGroupFooEvent",
"B:$dynamic,$bar.DynamicGroupBarEvent"));
}
@@ -2472,6 +2473,16 @@
"B:$dynamic,$foo.DynamicGroupFooEvent"));
}
+ // Enable exactly one dynamic category.
+ {
+ perfetto::protos::gen::TrackEventConfig te_cfg;
+ te_cfg.add_disabled_categories("*");
+ te_cfg.add_enabled_categories("dynamic");
+ auto slices = check_config(te_cfg);
+ EXPECT_THAT(slices, ElementsAre("B:$dynamic,$foo.DynamicGroupFooEvent",
+ "B:$dynamic,$bar.DynamicGroupBarEvent"));
+ }
+
// Enable two categories.
{
perfetto::protos::gen::TrackEventConfig te_cfg;
@@ -2497,7 +2508,8 @@
slices,
ElementsAre("B:foo.FooEvent", "B:bar.BarEvent", "B:foo,bar.MultiFooBar",
"B:baz,bar,quux.MultiBar", "B:red,green,blue,foo.MultiFoo",
- "B:test.TagEvent", "B:$dynamic,$foo.DynamicGroupFooEvent",
+ "B:red,green,blue,yellow.MultiNone", "B:test.TagEvent",
+ "B:$dynamic,$foo.DynamicGroupFooEvent",
"B:$dynamic,$bar.DynamicGroupBarEvent"));
}
@@ -2539,14 +2551,15 @@
te_cfg.add_enabled_tags("slow");
te_cfg.add_enabled_tags("debug");
auto slices = check_config(te_cfg);
- EXPECT_THAT(slices,
- ElementsAre("B:foo.FooEvent", "B:bar.BarEvent",
- "B:foo,bar.MultiFooBar", "B:baz,bar,quux.MultiBar",
- "B:red,green,blue,foo.MultiFoo", "B:cat.SlowEvent",
- "B:cat.verbose.DebugEvent", "B:test.TagEvent",
- "B:disabled-by-default-cat.SlowDisabledEvent",
- "B:$dynamic,$foo.DynamicGroupFooEvent",
- "B:$dynamic,$bar.DynamicGroupBarEvent"));
+ EXPECT_THAT(
+ slices,
+ ElementsAre("B:foo.FooEvent", "B:bar.BarEvent", "B:foo,bar.MultiFooBar",
+ "B:baz,bar,quux.MultiBar", "B:red,green,blue,foo.MultiFoo",
+ "B:red,green,blue,yellow.MultiNone", "B:cat.SlowEvent",
+ "B:cat.verbose.DebugEvent", "B:test.TagEvent",
+ "B:disabled-by-default-cat.SlowDisabledEvent",
+ "B:$dynamic,$foo.DynamicGroupFooEvent",
+ "B:$dynamic,$bar.DynamicGroupBarEvent"));
}
}
@@ -3246,10 +3259,19 @@
}
TEST_P(PerfettoApiTest, LegacyTraceEvents) {
+ auto is_new_session = [] {
+ bool result;
+ TRACE_EVENT_IS_NEW_TRACE(&result);
+ return result;
+ };
+
// Create a new trace session.
+ EXPECT_FALSE(is_new_session());
auto* tracing_session =
NewTraceWithCategories({"cat", TRACE_DISABLED_BY_DEFAULT("cat")});
tracing_session->get()->StartBlocking();
+ EXPECT_TRUE(is_new_session());
+ EXPECT_FALSE(is_new_session());
// Basic events.
TRACE_EVENT_INSTANT0("cat", "LegacyEvent", TRACE_EVENT_SCOPE_GLOBAL);
diff --git a/src/tracing/track_event_state_tracker.cc b/src/tracing/track_event_state_tracker.cc
index 4385f62..7cb1b09 100644
--- a/src/tracing/track_event_state_tracker.cc
+++ b/src/tracing/track_event_state_tracker.cc
@@ -143,8 +143,10 @@
parsed_event.name_hash = name_hash;
delegate.OnTrackEvent(*track, parsed_event);
- if (track_event.type() == protos::pbzero::TrackEvent::TYPE_SLICE_END)
+ if (track_event.type() == protos::pbzero::TrackEvent::TYPE_SLICE_END &&
+ !track->stack.empty()) {
track->stack.pop_back();
+ }
}
// static
diff --git a/src/tracing/virtual_destructors.cc b/src/tracing/virtual_destructors.cc
index d548c8e..d473551 100644
--- a/src/tracing/virtual_destructors.cc
+++ b/src/tracing/virtual_destructors.cc
@@ -26,7 +26,10 @@
namespace perfetto {
namespace internal {
-TracingTLS::~TracingTLS() = default;
+TracingTLS::~TracingTLS() {
+ // Avoid entering trace points while the thread is being torn down.
+ is_in_trace_point = true;
+}
} // namespace internal
diff --git a/ui/src/common/actions.ts b/ui/src/common/actions.ts
index 013b9a8..e982bc9 100644
--- a/ui/src/common/actions.ts
+++ b/ui/src/common/actions.ts
@@ -108,13 +108,15 @@
function rank(ts: TrackState): number[] {
const hpRank = rankIndex(ts.kind, highPriorityTrackOrder);
const lpRank = rankIndex(ts.kind, lowPriorityTrackOrder);
- return [hpRank, ts.trackKindPriority.valueOf(), lpRank];
+ // TODO(hjd): Create sortBy object on TrackState to avoid this cast.
+ const tid = (ts.config as {tid?: number}).tid || 0;
+ return [hpRank, ts.trackKindPriority.valueOf(), lpRank, tid];
}
function rankIndex<T>(element: T, array: T[]): number {
const index = array.indexOf(element);
- if (index === -1) return 0;
- return array.length - index;
+ if (index === -1) return array.length;
+ return index;
}
export const StateActions = {
@@ -278,7 +280,7 @@
const aRank = rank(state.tracks[a]);
const bRank = rank(state.tracks[b]);
for (let i = 0; i < aRank.length; i++) {
- if (aRank[i] !== bRank[i]) return bRank[i] - aRank[i];
+ if (aRank[i] !== bRank[i]) return aRank[i] - bRank[i];
}
const aName = state.tracks[a].name.toLocaleLowerCase();
diff --git a/ui/src/common/actions_unittest.ts b/ui/src/common/actions_unittest.ts
index 87bf309..8cf6acf 100644
--- a/ui/src/common/actions_unittest.ts
+++ b/ui/src/common/actions_unittest.ts
@@ -19,6 +19,7 @@
import {
PROCESS_SCHEDULING_TRACK_KIND
} from '../tracks/process_scheduling/common';
+import {THREAD_STATE_TRACK_KIND} from '../tracks/thread_state/common';
import {StateActions} from './actions';
import {
@@ -34,7 +35,8 @@
kind?: string,
trackGroup?: string,
trackKindPriority?: TrackKindPriority,
- name?: string
+ name?: string,
+ tid?: string
}): State {
return produce(state, draft => {
StateActions.addTrack(draft, {
@@ -46,7 +48,7 @@
TrackKindPriority.ORDINARY :
args.trackKindPriority,
trackGroup: args.trackGroup || SCROLLING_TRACK_GROUP,
- config: {}
+ config: {tid: args.tid || '0'}
});
});
}
@@ -392,3 +394,35 @@
expect(after.trackGroups['g'].tracks)
.toEqual(['a', 'b', 'b', 'c', 'd', 'e', 'f', 'g']);
});
+
+test('sortTracksByTidThenName', () => {
+ let state = createEmptyState();
+ state = fakeTrackGroup(state, {id: 'g', summaryTrackId: 'a'});
+ state = fakeTrack(state, {
+ id: 'a',
+ kind: SLICE_TRACK_KIND,
+ trackGroup: 'g',
+ name: 'aaa',
+ tid: '1'
+ });
+ state = fakeTrack(state, {
+ id: 'b',
+ kind: SLICE_TRACK_KIND,
+ trackGroup: 'g',
+ name: 'bbb',
+ tid: '2'
+ });
+ state = fakeTrack(state, {
+ id: 'c',
+ kind: THREAD_STATE_TRACK_KIND,
+ trackGroup: 'g',
+ name: 'ccc',
+ tid: '1'
+ });
+
+ const after = produce(state, draft => {
+ StateActions.sortThreadTracks(draft, {});
+ });
+
+ expect(after.trackGroups['g'].tracks).toEqual(['a', 'a', 'c', 'b']);
+});
diff --git a/ui/src/common/engine.ts b/ui/src/common/engine.ts
index c270010..3d387d4 100644
--- a/ui/src/common/engine.ts
+++ b/ui/src/common/engine.ts
@@ -18,7 +18,7 @@
RawQueryArgs,
RawQueryResult
} from './protos';
-import {slowlyCountRows} from './query_iterator';
+import {iter, NUM_NULL, slowlyCountRows, STR} from './query_iterator';
import {TimeSpan} from './time';
export interface LoadingTracker {
@@ -177,4 +177,25 @@
const res = (await this.queryOneRow(query));
return new TimeSpan(res[0] / 1e9, res[1] / 1e9);
}
+
+ async getTracingMetadataTimeBounds(): Promise<TimeSpan> {
+ const query = await this.query(`select name, int_value from metadata
+ where name = 'tracing_started_ns' or name = 'tracing_disabled_ns'
+ or name = 'all_data_source_started_ns'`);
+ let startBound = -Infinity;
+ let endBound = Infinity;
+ const it = iter({'name': STR, 'int_value': NUM_NULL}, query);
+ for (; it.valid(); it.next()) {
+ const columnName = it.row.name;
+ const timestamp = it.row.int_value;
+ if (timestamp === null) continue;
+ if (columnName === 'tracing_disabled_ns') {
+ endBound = Math.min(endBound, timestamp / 1e9);
+ } else {
+ startBound = Math.max(startBound, timestamp / 1e9);
+ }
+ }
+
+ return new TimeSpan(startBound, endBound);
+ }
}
diff --git a/ui/src/common/state.ts b/ui/src/common/state.ts
index a194042..71c3f2f 100644
--- a/ui/src/common/state.ts
+++ b/ui/src/common/state.ts
@@ -60,10 +60,10 @@
export type NewEngineMode = 'USE_HTTP_RPC_IF_AVAILABLE'|'FORCE_BUILTIN_WASM';
export enum TrackKindPriority {
- 'MAIN_THREAD' = 3,
- 'RENDER_THREAD' = 2,
- 'GPU_COMPLETION' = 1,
- 'ORDINARY' = 0
+ 'MAIN_THREAD' = 0,
+ 'RENDER_THREAD' = 1,
+ 'GPU_COMPLETION' = 2,
+ 'ORDINARY' = 3
}
export type HeapProfileFlamegraphViewingOption =
diff --git a/ui/src/controller/trace_controller.ts b/ui/src/controller/trace_controller.ts
index 2ef2012..65558b2 100644
--- a/ui/src/controller/trace_controller.ts
+++ b/ui/src/controller/trace_controller.ts
@@ -298,11 +298,26 @@
Actions.navigate({route: '/viewer'}),
];
+ let visibleStartSec = startSec;
+ let visibleEndSec = endSec;
+ const mdTime = await this.engine.getTracingMetadataTimeBounds();
+ // make sure the bounds hold
+ if (Math.max(visibleStartSec, mdTime.start - TRACE_MARGIN_TIME_S) <
+ Math.min(visibleEndSec, mdTime.end + TRACE_MARGIN_TIME_S)) {
+ visibleStartSec =
+ Math.max(visibleStartSec, mdTime.start - TRACE_MARGIN_TIME_S);
+ visibleEndSec = Math.min(visibleEndSec, mdTime.end + TRACE_MARGIN_TIME_S);
+ }
+
// We don't know the resolution at this point. However this will be
// replaced in 50ms so a guess is fine.
- const resolution = (traceTime.end - traceTime.start) / 1000;
- actions.push(Actions.setVisibleTraceTime(
- {...traceTimeState, lastUpdate: Date.now() / 1000, resolution}));
+ const resolution = (visibleStartSec - visibleEndSec) / 1000;
+ actions.push(Actions.setVisibleTraceTime({
+ startSec: visibleStartSec,
+ endSec: visibleEndSec,
+ lastUpdate: Date.now() / 1000,
+ resolution
+ }));
globals.dispatchMultiple(actions);
diff --git a/ui/src/controller/track_controller.ts b/ui/src/controller/track_controller.ts
index ff27d17..75ee194 100644
--- a/ui/src/controller/track_controller.ts
+++ b/ui/src/controller/track_controller.ts
@@ -16,7 +16,7 @@
import {Engine} from '../common/engine';
import {Registry} from '../common/registry';
import {TraceTime, TrackState} from '../common/state';
-import {toNs} from '../common/time';
+import {fromNs, toNs} from '../common/time';
import {LIMIT, TrackData} from '../common/track_data';
import {Controller} from './controller';
@@ -252,10 +252,18 @@
promise
.then(() => {
this.isSetup = true;
+ let resolution = visibleState.resolution;
+ // TODO(hjd): We shouldn't have to be so defensive here.
+ if (Math.log2(toNs(resolution)) % 1 !== 0) {
+ // resolution is in pixels per second so 1000 means
+ // 1px = 1ms.
+ resolution =
+ fromNs(Math.pow(2, Math.floor(Math.log2(toNs(1000)))));
+ }
return this.onBoundsChange(
visibleState.startSec - dur,
visibleState.endSec + dur,
- visibleState.resolution);
+ resolution);
})
.then(data => {
this.publish(data);
diff --git a/ui/src/controller/track_decider.ts b/ui/src/controller/track_decider.ts
index e320fd8..35e6ddb 100644
--- a/ui/src/controller/track_decider.ts
+++ b/ui/src/controller/track_decider.ts
@@ -48,6 +48,10 @@
import {PROCESS_SUMMARY_TRACK} from '../tracks/process_summary/common';
import {THREAD_STATE_TRACK_KIND} from '../tracks/thread_state/common';
+const MEM_DMA_COUNTER_NAME = 'mem.dma_heap';
+const MEM_DMA = 'mem.dma_buffer';
+const MEM_ION = 'mem.ion';
+
export async function decideTracks(
engineId: string, engine: Engine): Promise<DeferredAction[]> {
return (new TrackDecider(engineId, engine)).decideTracks();
@@ -296,6 +300,50 @@
}
}
+ async groupGlobalIonTracks(): Promise<void> {
+ const ionTracks: AddTrackArgs[] = [];
+ for (const track of this.tracksToAdd) {
+ const isIon = track.name.startsWith(MEM_ION);
+ const isDmaHeapCounter = track.name === MEM_DMA_COUNTER_NAME;
+ const isDmaBuffferSlices = track.name === MEM_DMA;
+ if (isIon || isDmaHeapCounter || isDmaBuffferSlices) {
+ ionTracks.push(track);
+ }
+ }
+
+ if (ionTracks.length === 0) {
+ return;
+ }
+
+ const id = uuidv4();
+ const summaryTrackId = uuidv4();
+ let foundSummary = false;
+
+ for (const track of ionTracks) {
+ if (!foundSummary &&
+ [MEM_DMA_COUNTER_NAME, MEM_ION].includes(track.name)) {
+ foundSummary = true;
+ track.id = summaryTrackId;
+ track.trackGroup = undefined;
+ } else {
+ track.trackGroup = id;
+ }
+ }
+
+ if (!foundSummary) {
+ return;
+ }
+
+ const addGroup = Actions.addTrackGroup({
+ engineId: this.engineId,
+ summaryTrackId,
+ name: MEM_DMA_COUNTER_NAME,
+ id,
+ collapsed: true,
+ });
+ this.addTrackGroupActions.push(addGroup);
+ }
+
async addLogsTrack(): Promise<void> {
const logCount =
await this.engine.query(`select count(1) from android_logs`);
@@ -410,7 +458,7 @@
trackGroup: uuid,
trackKindPriority:
TrackDecider.inferTrackKindPriority(threadName, tid, pid),
- config: {utid}
+ config: {utid, tid}
});
}
}
@@ -506,12 +554,7 @@
name,
trackKindPriority: TrackDecider.inferTrackKindPriority(threadName),
trackGroup: uuid,
- config: {
- name,
- trackId,
- startTs,
- endTs,
- }
+ config: {name, trackId, startTs, endTs, tid}
});
}
}
@@ -760,10 +803,7 @@
name,
trackGroup: uuid,
trackKindPriority,
- config: {
- trackId,
- maxDepth,
- }
+ config: {trackId, maxDepth, tid}
});
}
}
@@ -973,7 +1013,7 @@
kind,
trackKindPriority: TrackDecider.inferTrackKindPriority(threadName),
name: `${upid === null ? tid : pid} summary`,
- config: {pidForColor, upid, utid},
+ config: {pidForColor, upid, utid, tid},
});
const name = TrackDecider.getTrackName(
@@ -998,11 +1038,12 @@
await this.addGlobalAsyncTracks();
await this.addGpuFreqTracks();
await this.addGlobalCounterTracks();
+ await this.groupGlobalIonTracks();
// Create the per-process track groups. Note that this won't necessarily
// create a track per process. If a process has been completely idle and has
// no sched events, no track group will be emitted.
- // Will populate this.addTrackGroupActions().
+ // Will populate this.addTrackGroupActions
await this.addProcessTrackGroups();
await this.addProcessHeapProfileTracks();
diff --git a/ui/src/frontend/globals.ts b/ui/src/frontend/globals.ts
index 0c7474c..dd1e858 100644
--- a/ui/src/frontend/globals.ts
+++ b/ui/src/frontend/globals.ts
@@ -399,8 +399,23 @@
// window span). Therefore, zooming out by six levels is 1.1^6 ~= 2.
// Similarily, zooming in six levels is 0.9^6 ~= 0.5.
const pxToSec = this.frontendLocalState.timeScale.deltaPxToDuration(1);
+ // TODO(b/186265930): Remove once fixed:
+ if (!isFinite(pxToSec)) {
+ // Resolution is in pixels per second so 1000 means 1px = 1ms.
+ console.error(`b/186265930: Bad pxToSec suppressed ${pxToSec}`);
+ return fromNs(Math.pow(2, Math.floor(Math.log2(toNs(1000)))));
+ }
const pxToNs = Math.max(toNs(pxToSec), 1);
- return fromNs(Math.pow(2, Math.floor(Math.log2(pxToNs))));
+ const resolution = fromNs(Math.pow(2, Math.floor(Math.log2(pxToNs))));
+ const log2 = Math.log2(toNs(resolution));
+ if (log2 % 1 !== 0) {
+ throw new Error(`Resolution should be a power of two.
+ pxToSec: ${pxToSec},
+ pxToNs: ${pxToNs},
+ resolution: ${resolution},
+ log2: ${Math.log2(toNs(resolution))}`);
+ }
+ return resolution;
}
makeSelection(action: DeferredAction<{}>, tabToOpen = 'current_selection') {
diff --git a/ui/src/frontend/notes_panel.ts b/ui/src/frontend/notes_panel.ts
index 017deea..55dc47a 100644
--- a/ui/src/frontend/notes_panel.ts
+++ b/ui/src/frontend/notes_panel.ts
@@ -94,6 +94,8 @@
for (const note of Object.values(globals.state.notes)) {
const timestamp = getStartTimestamp(note);
+ // TODO(hjd): We should still render area selection marks in viewport is
+ // *within* the area (e.g. both lhs and rhs are out of bounds).
if ((note.noteType !== 'AREA' && !timeScale.timeInBounds(timestamp)) ||
(note.noteType === 'AREA' &&
!timeScale.timeInBounds(globals.state.areas[note.areaId].endSec) &&
@@ -182,12 +184,12 @@
ctx.stroke();
// Start line after track shell section, join triangles.
- const startDraw =
- Math.max(
- x,
- globals.frontendLocalState.timeScale.startPx + TRACK_SHELL_WIDTH) -
- 1;
- ctx.fillRect(startDraw, topOffset - 1, xEnd - startDraw + 1, 1);
+ const startDraw = Math.max(
+ x, globals.frontendLocalState.timeScale.startPx + TRACK_SHELL_WIDTH);
+ ctx.beginPath();
+ ctx.moveTo(startDraw, topOffset);
+ ctx.lineTo(xEnd, topOffset);
+ ctx.stroke();
}
private drawFlag(
diff --git a/ui/src/frontend/record_page.ts b/ui/src/frontend/record_page.ts
index f493630..182a66b 100644
--- a/ui/src/frontend/record_page.ts
+++ b/ui/src/frontend/record_page.ts
@@ -56,44 +56,43 @@
const POLL_INTERVAL_MS = [250, 500, 1000, 2500, 5000, 30000, 60000];
const ATRACE_CATEGORIES = new Map<string, string>();
+ATRACE_CATEGORIES.set('adb', 'ADB');
+ATRACE_CATEGORIES.set('aidl', 'AIDL calls');
+ATRACE_CATEGORIES.set('am', 'Activity Manager');
+ATRACE_CATEGORIES.set('audio', 'Audio');
+ATRACE_CATEGORIES.set('binder_driver', 'Binder Kernel driver');
+ATRACE_CATEGORIES.set('binder_lock', 'Binder global lock trace');
+ATRACE_CATEGORIES.set('bionic', 'Bionic C library');
+ATRACE_CATEGORIES.set('camera', 'Camera');
+ATRACE_CATEGORIES.set('dalvik', 'ART & Dalvik');
+ATRACE_CATEGORIES.set('database', 'Database');
ATRACE_CATEGORIES.set('gfx', 'Graphics');
+ATRACE_CATEGORIES.set('hal', 'Hardware Modules');
ATRACE_CATEGORIES.set('input', 'Input');
+ATRACE_CATEGORIES.set('network', 'Network');
+ATRACE_CATEGORIES.set('nnapi', 'Neural Network API');
+ATRACE_CATEGORIES.set('pm', 'Package Manager');
+ATRACE_CATEGORIES.set('power', 'Power Management');
+ATRACE_CATEGORIES.set('res', 'Resource Loading');
+ATRACE_CATEGORIES.set('rro', 'Resource Overlay');
+ATRACE_CATEGORIES.set('rs', 'RenderScript');
+ATRACE_CATEGORIES.set('sm', 'Sync Manager');
+ATRACE_CATEGORIES.set('ss', 'System Server');
+ATRACE_CATEGORIES.set('vibrator', 'Vibrator');
+ATRACE_CATEGORIES.set('video', 'Video');
ATRACE_CATEGORIES.set('view', 'View System');
ATRACE_CATEGORIES.set('webview', 'WebView');
ATRACE_CATEGORIES.set('wm', 'Window Manager');
-ATRACE_CATEGORIES.set('am', 'Activity Manager');
-ATRACE_CATEGORIES.set('sm', 'Sync Manager');
-ATRACE_CATEGORIES.set('audio', 'Audio');
-ATRACE_CATEGORIES.set('video', 'Video');
-ATRACE_CATEGORIES.set('camera', 'Camera');
-ATRACE_CATEGORIES.set('hal', 'Hardware Modules');
-ATRACE_CATEGORIES.set('res', 'Resource Loading');
-ATRACE_CATEGORIES.set('dalvik', 'ART & Dalvik');
-ATRACE_CATEGORIES.set('rs', 'RenderScript');
-ATRACE_CATEGORIES.set('bionic', 'Bionic C library');
-ATRACE_CATEGORIES.set('gfx', 'Graphics');
-ATRACE_CATEGORIES.set('power', 'Power Management');
-ATRACE_CATEGORIES.set('pm', 'Package Manager');
-ATRACE_CATEGORIES.set('ss', 'System Server');
-ATRACE_CATEGORIES.set('database', 'Database');
-ATRACE_CATEGORIES.set('network', 'Network');
-ATRACE_CATEGORIES.set('adb', 'ADB');
-ATRACE_CATEGORIES.set('vibrator', 'Vibrator');
-ATRACE_CATEGORIES.set('aidl', 'AIDL calls');
-ATRACE_CATEGORIES.set('nnapi', 'Neural Network API');
-ATRACE_CATEGORIES.set('rro', 'Resource Overlay');
-ATRACE_CATEGORIES.set('binder_driver', 'Binder Kernel driver');
-ATRACE_CATEGORIES.set('binder_lock', 'Binder global lock trace');
const LOG_BUFFERS = new Map<string, string>();
-LOG_BUFFERS.set('LID_DEFAULT', 'Main');
-LOG_BUFFERS.set('LID_RADIO', 'Radio');
-LOG_BUFFERS.set('LID_EVENTS', 'Binary events');
-LOG_BUFFERS.set('LID_SYSTEM', 'System');
LOG_BUFFERS.set('LID_CRASH', 'Crash');
-LOG_BUFFERS.set('LID_STATS', 'Stats');
-LOG_BUFFERS.set('LID_SECURITY', 'Security');
+LOG_BUFFERS.set('LID_DEFAULT', 'Main');
+LOG_BUFFERS.set('LID_EVENTS', 'Binary events');
LOG_BUFFERS.set('LID_KERNEL', 'Kernel');
+LOG_BUFFERS.set('LID_RADIO', 'Radio');
+LOG_BUFFERS.set('LID_SECURITY', 'Security');
+LOG_BUFFERS.set('LID_STATS', 'Stats');
+LOG_BUFFERS.set('LID_SYSTEM', 'System');
const FTRACE_CATEGORIES = new Map<string, string>();
FTRACE_CATEGORIES.set('binder/*', 'binder');
diff --git a/ui/src/frontend/record_widgets.ts b/ui/src/frontend/record_widgets.ts
index fd919a8..b107080 100644
--- a/ui/src/frontend/record_widgets.ts
+++ b/ui/src/frontend/record_widgets.ts
@@ -251,7 +251,9 @@
const options: m.Children = [];
const selItems = attrs.get(globals.state.recordConfig);
let numSelected = 0;
- for (const [key, label] of attrs.options) {
+ const entries = [...attrs.options.entries()];
+ entries.sort((a, b) => a[1].localeCompare(b[1]));
+ for (const [key, label] of entries) {
const opts = {value: key, selected: false};
if (selItems.includes(key)) {
opts.selected = true;
diff --git a/ui/src/frontend/sidebar.ts b/ui/src/frontend/sidebar.ts
index d46074d..b9f8d0f 100644
--- a/ui/src/frontend/sidebar.ts
+++ b/ui/src/frontend/sidebar.ts
@@ -852,9 +852,12 @@
}
function createTraceLink(title: string, url: string) {
+ if (url === '') {
+ return m('a.trace-file-name', title);
+ }
const linkProps = {
href: url,
- title: url !== '' ? 'Click to copy the URL' : '',
+ title: 'Click to copy the URL',
target: '_blank',
onclick: (e: Event) => {
e.preventDefault();
diff --git a/ui/src/tracks/async_slices/controller.ts b/ui/src/tracks/async_slices/controller.ts
index 96bd6e4..50a074a 100644
--- a/ui/src/tracks/async_slices/controller.ts
+++ b/ui/src/tracks/async_slices/controller.ts
@@ -38,7 +38,7 @@
if (this.maxDurNs === 0) {
const maxDurResult = await this.query(`
- select max(dur)
+ select max(iif(dur = -1, (SELECT end_ts FROM trace_bounds) - ts, dur))
from experimental_slice_layout
where filter_track_ids = '${this.config.trackIds.join(',')}'
`);
@@ -51,7 +51,7 @@
SELECT
(ts + ${bucketNs / 2}) / ${bucketNs} * ${bucketNs} as tsq,
ts,
- max(dur) as dur,
+ max(iif(dur = -1, (SELECT end_ts FROM trace_bounds) - ts, dur)) as dur,
layout_depth,
name,
id,