Merge "UI: reintroduce "table-layout: fixed" for aggregation details panel"
diff --git a/gn/standalone/libc++/BUILD.gn b/gn/standalone/libc++/BUILD.gn
index 809a448..f6b6131 100644
--- a/gn/standalone/libc++/BUILD.gn
+++ b/gn/standalone/libc++/BUILD.gn
@@ -20,6 +20,13 @@
"_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
"_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS",
]
+ if (is_debug) {
+ # libc++ has two levels of debug mode. Setting _LIBCPP_DEBUG to zero
+ # enables most assertions. Setting it to one additionally enables iterator
+ # debugging, but that seems to require some extra link-time dependencies.
+ # See https://libcxx.llvm.org/docs/DesignDocs/DebugMode.html
+ defines += [ "_LIBCPP_DEBUG=0" ]
+ }
cflags_cc = [
"-nostdinc++",
"-isystem" + rebase_path("$libcxx_prefix/include", root_build_dir),
@@ -28,9 +35,7 @@
# Avoid linking both libc++ and libstdc++.
ldflags = [ "-nostdlib++" ]
- libs = [
- "dl", # libdl: dynamic linking.
- ]
+ libs = [ "dl" ] # libdl: dynamic linking.
}
}
diff --git a/src/kallsyms/kernel_symbol_map.cc b/src/kallsyms/kernel_symbol_map.cc
index 920c897..126d6dd 100644
--- a/src/kallsyms/kernel_symbol_map.cc
+++ b/src/kallsyms/kernel_symbol_map.cc
@@ -202,7 +202,7 @@
*(tok_wptr++) = token.at(i) & 0x7f;
}
*(tok_wptr++) = static_cast<char>(token.at(token_size - 1) | 0x80);
- PERFETTO_DCHECK(tok_wptr == &buf_[buf_.size()]);
+ PERFETTO_DCHECK(tok_wptr == buf_.data() + buf_.size());
return id;
}
@@ -394,7 +394,7 @@
uint32_t addr = it->first;
uint32_t off = it->second;
const uint8_t* rdptr = &buf_[off];
- const uint8_t* const buf_end = &buf_[buf_.size()];
+ const uint8_t* const buf_end = buf_.data() + buf_.size();
bool parsing_addr = true;
const uint8_t* next_rdptr = nullptr;
uint64_t sym_start_addr = 0;
diff --git a/src/perfetto_cmd/pbtxt_to_pb.cc b/src/perfetto_cmd/pbtxt_to_pb.cc
index f4f0c00..b78d938 100644
--- a/src/perfetto_cmd/pbtxt_to_pb.cc
+++ b/src/perfetto_cmd/pbtxt_to_pb.cc
@@ -47,12 +47,20 @@
namespace {
+constexpr bool IsOct(char c) {
+ return (c >= '0' && c <= '7');
+}
+
+constexpr bool IsDigit(char c) {
+ return (c >= '0' && c <= '9');
+}
+
constexpr bool IsIdentifierStart(char c) {
return ('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z') || c == '_';
}
constexpr bool IsIdentifierBody(char c) {
- return IsIdentifierStart(c) || isdigit(c);
+ return IsIdentifierStart(c) || IsDigit(c);
}
const char* FieldToTypeName(const FieldDescriptorProto* field) {
@@ -150,19 +158,22 @@
}
void NumericField(Token key, Token value) {
- const FieldDescriptorProto* field = FindFieldByName(
- key, value,
- {
- FieldDescriptorProto::TYPE_UINT64,
- FieldDescriptorProto::TYPE_UINT32, FieldDescriptorProto::TYPE_INT64,
- FieldDescriptorProto::TYPE_SINT64, FieldDescriptorProto::TYPE_INT32,
- FieldDescriptorProto::TYPE_SINT32,
- FieldDescriptorProto::TYPE_FIXED64,
- FieldDescriptorProto::TYPE_SFIXED64,
- FieldDescriptorProto::TYPE_FIXED32,
- FieldDescriptorProto::TYPE_SFIXED32,
- FieldDescriptorProto::TYPE_DOUBLE, FieldDescriptorProto::TYPE_FLOAT,
- });
+ const FieldDescriptorProto* field =
+ FindFieldByName(key, value,
+ {
+ FieldDescriptorProto::TYPE_UINT64,
+ FieldDescriptorProto::TYPE_UINT32,
+ FieldDescriptorProto::TYPE_INT64,
+ FieldDescriptorProto::TYPE_SINT64,
+ FieldDescriptorProto::TYPE_INT32,
+ FieldDescriptorProto::TYPE_SINT32,
+ FieldDescriptorProto::TYPE_FIXED64,
+ FieldDescriptorProto::TYPE_SFIXED64,
+ FieldDescriptorProto::TYPE_FIXED32,
+ FieldDescriptorProto::TYPE_SFIXED32,
+ FieldDescriptorProto::TYPE_DOUBLE,
+ FieldDescriptorProto::TYPE_FLOAT,
+ });
if (!field)
return;
const auto& field_type = field->type();
@@ -202,11 +213,12 @@
}
void StringField(Token key, Token value) {
- const FieldDescriptorProto* field = FindFieldByName(
- key, value,
- {
- FieldDescriptorProto::TYPE_STRING, FieldDescriptorProto::TYPE_BYTES,
- });
+ const FieldDescriptorProto* field =
+ FindFieldByName(key, value,
+ {
+ FieldDescriptorProto::TYPE_STRING,
+ FieldDescriptorProto::TYPE_BYTES,
+ });
if (!field)
return;
uint32_t field_id = static_cast<uint32_t>(field->number());
@@ -217,15 +229,16 @@
std::unique_ptr<char, base::FreeDeleter> s(
static_cast<char*>(malloc(value.size())));
size_t j = 0;
+ const char* const txt = value.txt.data();
for (size_t i = 0; i < value.size(); i++) {
- char c = value.txt.data()[i];
+ char c = txt[i];
if (c == '\\') {
if (i + 1 >= value.size()) {
// This should be caught by the lexer.
PERFETTO_FATAL("Escape at end of string.");
return;
}
- char next = value.txt.data()[++i];
+ char next = txt[++i];
switch (next) {
case '\\':
case '\'':
@@ -254,6 +267,44 @@
case 'v':
s.get()[j++] = '\v';
break;
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9': {
+ // Cases 8 and 9 are not really required and are only added for the
+ // sake of error reporting.
+ bool oct_err = false;
+ if (i + 2 >= value.size() || !IsOct(txt[i + 1]) ||
+ !IsOct(txt[i + 2])) {
+ oct_err = true;
+ } else {
+ char buf[4]{next, txt[++i], txt[++i], '\0'};
+ auto octval = base::CStringToUInt32(buf, 8);
+ if (!octval.has_value() || *octval > 0xff) {
+ oct_err = true;
+ } else {
+ s.get()[j++] = static_cast<char>(static_cast<uint8_t>(*octval));
+ }
+ }
+ if (oct_err) {
+ AddError(value,
+ "Malformed string escape in $k in proto $n on '$v'. "
+ "\\NNN escapes must be exactly three octal digits <= "
+ "\\377 (0xff).",
+ std::map<std::string, std::string>{
+ {"$k", key.ToStdString()},
+ {"$n", descriptor_name()},
+ {"$v", value.ToStdString()},
+ });
+ }
+ break;
+ }
default:
AddError(value,
"Unknown string escape in $k in "
@@ -273,11 +324,12 @@
}
void IdentifierField(Token key, Token value) {
- const FieldDescriptorProto* field = FindFieldByName(
- key, value,
- {
- FieldDescriptorProto::TYPE_BOOL, FieldDescriptorProto::TYPE_ENUM,
- });
+ const FieldDescriptorProto* field =
+ FindFieldByName(key, value,
+ {
+ FieldDescriptorProto::TYPE_BOOL,
+ FieldDescriptorProto::TYPE_ENUM,
+ });
if (!field)
return;
uint32_t field_id = static_cast<uint32_t>(field->number());
@@ -415,7 +467,8 @@
if (!field_descriptor) {
AddError(key, "No field named \"$n\" in proto $p",
{
- {"$n", field_name}, {"$p", descriptor_name()},
+ {"$n", field_name},
+ {"$p", descriptor_name()},
});
return nullptr;
}
@@ -549,7 +602,7 @@
state = kReadingStringValue;
continue;
}
- if (c == '-' || isdigit(c) || c == '.') {
+ if (c == '-' || IsDigit(c) || c == '.') {
state = kReadingNumericValue;
continue;
}
@@ -576,7 +629,7 @@
delegate->NumericField(key, value);
continue;
}
- if (isdigit(c) || c == '.')
+ if (IsDigit(c) || c == '.')
continue;
break;
diff --git a/src/perfetto_cmd/pbtxt_to_pb_unittest.cc b/src/perfetto_cmd/pbtxt_to_pb_unittest.cc
index 00dab16..251c8f0 100644
--- a/src/perfetto_cmd/pbtxt_to_pb_unittest.cc
+++ b/src/perfetto_cmd/pbtxt_to_pb_unittest.cc
@@ -403,6 +403,7 @@
ftrace_events: "newline\nnewline"
ftrace_events: "\"quoted\""
ftrace_events: "\a\b\f\n\r\t\v\\\'\"\?"
+ ftrace_events: "\0127_\03422.\177"
}
}
}
@@ -417,6 +418,7 @@
EXPECT_THAT(events, Contains("newline\nnewline"));
EXPECT_THAT(events, Contains("\"quoted\""));
EXPECT_THAT(events, Contains("\a\b\f\n\r\t\v\\\'\"\?"));
+ EXPECT_THAT(events, Contains("\0127_\03422.\177"));
}
TEST(PbtxtToPb, UnknownField) {
diff --git a/src/profiling/memory/bookkeeping.h b/src/profiling/memory/bookkeeping.h
index 6aa9086..77a1319 100644
--- a/src/profiling/memory/bookkeeping.h
+++ b/src/profiling/memory/bookkeeping.h
@@ -193,8 +193,9 @@
void ClearFrameCache() { frame_cache_.clear(); }
- uint64_t committed_timestamp() { return committed_timestamp_; }
- uint64_t max_timestamp() { return max_timestamp_; }
+ uint64_t dump_timestamp() {
+ return dump_at_max_mode_ ? max_timestamp_ : committed_timestamp_;
+ }
uint64_t GetSizeForTesting(const std::vector<unwindstack::FrameData>& stack,
std::vector<std::string> build_ids);
diff --git a/src/profiling/memory/bookkeeping_unittest.cc b/src/profiling/memory/bookkeeping_unittest.cc
index 79e0183..4dc16ca 100644
--- a/src/profiling/memory/bookkeeping_unittest.cc
+++ b/src/profiling/memory/bookkeeping_unittest.cc
@@ -145,7 +145,7 @@
sequence_number++;
hd.RecordMalloc(stack2(), DummyBuildIds(stack2().size()), 0x2, 1, 2,
sequence_number, 100 * sequence_number);
- ASSERT_EQ(hd.max_timestamp(), 200u);
+ ASSERT_EQ(hd.dump_timestamp(), 200u);
ASSERT_EQ(hd.GetMaxForTesting(stack(), DummyBuildIds(stack().size())), 5u);
ASSERT_EQ(hd.GetMaxForTesting(stack2(), DummyBuildIds(stack2().size())), 2u);
ASSERT_EQ(hd.GetMaxCountForTesting(stack(), DummyBuildIds(stack().size())),
@@ -171,7 +171,7 @@
sequence_number, 100 * sequence_number);
sequence_number++;
hd.RecordFree(0x2, sequence_number, 100 * sequence_number);
- EXPECT_EQ(hd.max_timestamp(), 400u);
+ EXPECT_EQ(hd.dump_timestamp(), 400u);
EXPECT_EQ(hd.GetMaxForTesting(stack(), DummyBuildIds(stack().size())), 0u);
EXPECT_EQ(hd.GetMaxForTesting(stack2(), DummyBuildIds(stack2().size())), 15u);
EXPECT_EQ(hd.GetMaxForTesting(stack3(), DummyBuildIds(stack3().size())), 15u);
diff --git a/src/profiling/memory/heapprofd_producer.cc b/src/profiling/memory/heapprofd_producer.cc
index dfc6922..9865a47 100644
--- a/src/profiling/memory/heapprofd_producer.cc
+++ b/src/profiling/memory/heapprofd_producer.cc
@@ -699,39 +699,26 @@
bool from_startup = data_source->signaled_pids.find(pid) ==
data_source->signaled_pids.cend();
- uint64_t dump_timestamp;
- if (data_source->config.dump_at_max())
- dump_timestamp = heap_info.heap_tracker.max_timestamp();
- else
- dump_timestamp = heap_info.heap_tracker.committed_timestamp();
- const char* heap_name = nullptr;
- if (!heap_info.heap_name.empty())
- heap_name = heap_info.heap_name.c_str();
- uint64_t sampling_interval = heap_info.sampling_interval;
- uint64_t orig_sampling_interval = heap_info.orig_sampling_interval;
-
- auto new_heapsamples =
- [pid, from_startup, dump_timestamp, process_state, data_source,
- heap_name, sampling_interval,
- orig_sampling_interval](ProfilePacket::ProcessHeapSamples* proto) {
- proto->set_pid(static_cast<uint64_t>(pid));
- proto->set_timestamp(dump_timestamp);
- proto->set_from_startup(from_startup);
- proto->set_disconnected(process_state->disconnected);
- proto->set_buffer_overran(process_state->error_state ==
- SharedRingBuffer::kHitTimeout);
- proto->set_client_error(
- ErrorStateToProto(process_state->error_state));
- proto->set_buffer_corrupted(process_state->buffer_corrupted);
- proto->set_hit_guardrail(data_source->hit_guardrail);
- if (heap_name)
- proto->set_heap_name(heap_name);
- proto->set_sampling_interval_bytes(sampling_interval);
- proto->set_orig_sampling_interval_bytes(orig_sampling_interval);
- auto* stats = proto->set_stats();
- SetStats(stats, *process_state);
- };
+ auto new_heapsamples = [pid, from_startup, process_state, data_source,
+ &heap_info](
+ ProfilePacket::ProcessHeapSamples* proto) {
+ proto->set_pid(static_cast<uint64_t>(pid));
+ proto->set_timestamp(heap_info.heap_tracker.dump_timestamp());
+ proto->set_from_startup(from_startup);
+ proto->set_disconnected(process_state->disconnected);
+ proto->set_buffer_overran(process_state->error_state ==
+ SharedRingBuffer::kHitTimeout);
+ proto->set_client_error(ErrorStateToProto(process_state->error_state));
+ proto->set_buffer_corrupted(process_state->buffer_corrupted);
+ proto->set_hit_guardrail(data_source->hit_guardrail);
+ if (!heap_info.heap_name.empty())
+ proto->set_heap_name(heap_info.heap_name.c_str());
+ proto->set_sampling_interval_bytes(heap_info.sampling_interval);
+ proto->set_orig_sampling_interval_bytes(heap_info.orig_sampling_interval);
+ auto* stats = proto->set_stats();
+ SetStats(stats, *process_state);
+ };
DumpState dump_state(data_source->trace_writer.get(),
std::move(new_heapsamples),
diff --git a/src/profiling/memory/wire_protocol.cc b/src/profiling/memory/wire_protocol.cc
index 29ea042..9d30beb 100644
--- a/src/profiling/memory/wire_protocol.cc
+++ b/src/profiling/memory/wire_protocol.cc
@@ -58,6 +58,10 @@
template <typename F>
int64_t WithBuffer(SharedRingBuffer* shmem, size_t total_size, F fn) {
+ if (total_size > shmem->size()) {
+ errno = EMSGSIZE;
+ return -1;
+ }
SharedRingBuffer::Buffer buf;
{
ScopedSpinlock lock = shmem->AcquireLock(ScopedSpinlock::Mode::Try);
diff --git a/src/protozero/filtering/BUILD.gn b/src/protozero/filtering/BUILD.gn
index b438929..ffd1c8c 100644
--- a/src/protozero/filtering/BUILD.gn
+++ b/src/protozero/filtering/BUILD.gn
@@ -83,7 +83,6 @@
":bytecode_common",
":bytecode_generator",
":bytecode_parser",
- ":filter_util",
":message_filter",
"..:protozero",
"../../../gn:default_deps",
@@ -95,10 +94,19 @@
sources = [
"filter_bytecode_generator_unittest.cc",
"filter_bytecode_parser_unittest.cc",
- "filter_util_unittest.cc",
- "message_filter_unittest.cc",
"message_tokenizer_unittest.cc",
]
+
+ # On chromium component build we cannot have a test target depening boh on
+ # protobuf-full and protobuf-lite. Just skip those targets there.
+ # See also https://crug.com/1210223 .
+ if (perfetto_build_standalone || perfetto_build_with_android) {
+ deps += [ ":filter_util" ]
+ sources += [
+ "filter_util_unittest.cc",
+ "message_filter_unittest.cc",
+ ]
+ }
}
if (enable_perfetto_benchmarks) {
diff --git a/src/protozero/filtering/filter_util.cc b/src/protozero/filtering/filter_util.cc
index c17b620..94d436e 100644
--- a/src/protozero/filtering/filter_util.cc
+++ b/src/protozero/filtering/filter_util.cc
@@ -23,6 +23,7 @@
#include <google/protobuf/compiler/importer.h>
+#include "perfetto/base/build_config.h"
#include "perfetto/ext/base/file_utils.h"
#include "perfetto/ext/base/getopt.h"
#include "perfetto/ext/base/string_utils.h"
@@ -68,13 +69,34 @@
bool FilterUtil::LoadMessageDefinition(const std::string& proto_file,
const std::string& root_message,
const std::string& proto_dir_path) {
+ // The protobuf compiler doesn't like backslashes and prints an error like:
+ // Error C:\it7mjanpw3\perfetto-a16500 -1:0: Backslashes, consecutive slashes,
+ // ".", or ".." are not allowed in the virtual path.
+ // Given that C:\foo\bar is a legit path on windows, fix it at this level
+ // because the problem is really the protobuf compiler being too picky.
+ static auto normalize_for_win = [](const std::string& path) {
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+ return perfetto::base::ReplaceAll(path, "\\", "/");
+#else
+ return path;
+#endif
+ };
+
google::protobuf::compiler::DiskSourceTree dst;
- dst.MapPath("/", "/");
- dst.MapPath("", proto_dir_path);
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+ // If the path is absolute, maps "C:/" -> "C:/" (without hardcoding 'C').
+ if (proto_file.size() > 3 && proto_file[1] == ':') {
+ char win_drive[4];
+ sprintf(win_drive, "%c:/", proto_file[0]);
+ dst.MapPath(win_drive, win_drive);
+ }
+#endif
+ dst.MapPath("/", "/"); // We might still need this on Win under cygwin.
+ dst.MapPath("", normalize_for_win(proto_dir_path));
MultiFileErrorCollectorImpl mfe;
google::protobuf::compiler::Importer importer(&dst, &mfe);
const google::protobuf::FileDescriptor* root_file =
- importer.Import(proto_file);
+ importer.Import(normalize_for_win(proto_file));
const google::protobuf::Descriptor* root_msg = nullptr;
if (!root_message.empty()) {
root_msg = importer.pool()->FindMessageTypeByName(root_message);
diff --git a/src/trace_processor/metrics/android/android_startup_launches.sql b/src/trace_processor/metrics/android/android_startup_launches.sql
index 2cfb4b9..c5829d7 100644
--- a/src/trace_processor/metrics/android/android_startup_launches.sql
+++ b/src/trace_processor/metrics/android/android_startup_launches.sql
@@ -104,11 +104,11 @@
DROP TABLE IF EXISTS launch_processes;
CREATE TABLE launch_processes(launch_id INT, upid BIG INT);
--- We make the (not always correct) simplification that process == package
INSERT INTO launch_processes
SELECT launches.id, process.upid
FROM launches
- JOIN process ON launches.package = process.name
+ LEFT JOIN package_list ON (launches.package = package_list.package_name)
+ JOIN process ON (launches.package = process.name OR process.uid = package_list.uid)
JOIN thread ON (process.upid = thread.upid AND process.pid = thread.tid)
WHERE (process.start_ts IS NULL OR process.start_ts < launches.ts_end)
AND (thread.end_ts IS NULL OR thread.end_ts > launches.ts_end)
diff --git a/src/trace_processor/metrics/metrics.cc b/src/trace_processor/metrics/metrics.cc
index e8539cc..fe2492c 100644
--- a/src/trace_processor/metrics/metrics.cc
+++ b/src/trace_processor/metrics/metrics.cc
@@ -264,6 +264,14 @@
return util::OkStatus();
}
+ if (size > protozero::proto_utils::kMaxMessageLength) {
+ return util::ErrStatus(
+ "Message passed to field %s in proto message %s has size %zu which is "
+ "larger than the maximum allowed message size %zu",
+ field.name().c_str(), descriptor_->full_name().c_str(), size,
+ protozero::proto_utils::kMaxMessageLength);
+ }
+
protos::pbzero::ProtoBuilderResult::Decoder decoder(ptr, size);
if (decoder.is_repeated()) {
return util::ErrStatus("Cannot handle nested repeated messages in field %s",
@@ -305,6 +313,14 @@
util::Status ProtoBuilder::AppendRepeated(const FieldDescriptor& field,
const uint8_t* ptr,
size_t size) {
+ if (size > protozero::proto_utils::kMaxMessageLength) {
+ return util::ErrStatus(
+ "Message passed to field %s in proto message %s has size %zu which is "
+ "larger than the maximum allowed message size %zu",
+ field.name().c_str(), descriptor_->full_name().c_str(), size,
+ protozero::proto_utils::kMaxMessageLength);
+ }
+
protos::pbzero::ProtoBuilderResult::Decoder decoder(ptr, size);
if (!decoder.is_repeated()) {
return util::ErrStatus(
diff --git a/tools/gen_android_bp b/tools/gen_android_bp
index 9c4f408..5ae4b85 100755
--- a/tools/gen_android_bp
+++ b/tools/gen_android_bp
@@ -166,8 +166,8 @@
# Skip test data files that require GCS. They are only for benchmarks.
# We don't run benchmarks in the android tree.
continue
- if line.endswith('/'):
- yield line + '**/*'
+ if line.endswith('/.'):
+ yield line[:-1] + '**/*'
else:
yield line
diff --git a/tools/test_data.txt b/tools/test_data.txt
index 55ee848..0585217 100644
--- a/tools/test_data.txt
+++ b/tools/test_data.txt
@@ -1,7 +1,10 @@
# List of test deps that should be pushed on the device. Paths are relative
# to the root.
-src/traced/probes/filesystem/testdata/
-src/traced/probes/ftrace/test/data/
+# The trailing /. at the end of a directory is to avoid creating a nesting
+# directory when pushing a 2nd-time. adb push has a slightly different behavior
+# than `cp` on directoriesm, trailing slash is not enough.
+src/traced/probes/filesystem/testdata/.
+src/traced/probes/ftrace/test/data/.
test/data/android_log_ring_buffer_mode.pb
test/data/example_android_trace_30s.pb
test/data/full_trace_filter.bytecode