Merge "Add tools/busy_threads for spawning N busy-wait threads for debugging"
diff --git a/Android.bp b/Android.bp
index 110ac0a..5e92386 100644
--- a/Android.bp
+++ b/Android.bp
@@ -430,6 +430,7 @@
"src/perfetto_cmd/pbtxt_to_pb.cc",
"src/perfetto_cmd/perfetto_cmd.cc",
"src/perfetto_cmd/rate_limiter.cc",
+ "src/perfetto_cmd/trigger_producer.cc",
"src/protozero/message.cc",
"src/protozero/message_handle.cc",
"src/protozero/proto_decoder.cc",
@@ -464,9 +465,6 @@
"src/tracing/core/trace_writer_impl.cc",
"src/tracing/core/tracing_service_impl.cc",
"src/tracing/core/virtual_destructors.cc",
- "src/tracing/ipc/consumer/consumer_ipc_client_impl.cc",
- "src/tracing/ipc/default_socket.cc",
- "src/tracing/ipc/posix_shared_memory.cc",
],
shared_libs: [
"libandroid",
@@ -478,6 +476,7 @@
],
static_libs: [
"libgtest_prod",
+ "perfetto_src_tracing_ipc",
],
generated_headers: [
"perfetto_protos_perfetto_common_lite_gen_headers",
@@ -2900,6 +2899,7 @@
"src/perfetto_cmd/perfetto_cmd.cc",
"src/perfetto_cmd/rate_limiter.cc",
"src/perfetto_cmd/rate_limiter_unittest.cc",
+ "src/perfetto_cmd/trigger_producer.cc",
"src/profiling/memory/bookkeeping.cc",
"src/profiling/memory/bookkeeping_unittest.cc",
"src/profiling/memory/client.cc",
@@ -3026,9 +3026,6 @@
"src/tracing/core/tracing_service_impl.cc",
"src/tracing/core/tracing_service_impl_unittest.cc",
"src/tracing/core/virtual_destructors.cc",
- "src/tracing/ipc/consumer/consumer_ipc_client_impl.cc",
- "src/tracing/ipc/default_socket.cc",
- "src/tracing/ipc/posix_shared_memory.cc",
"src/tracing/ipc/posix_shared_memory_unittest.cc",
"src/tracing/test/aligned_buffer_test.cc",
"src/tracing/test/fake_packet.cc",
diff --git a/include/perfetto/trace_processor/trace_processor.h b/include/perfetto/trace_processor/trace_processor.h
index b511f46..e67580c 100644
--- a/include/perfetto/trace_processor/trace_processor.h
+++ b/include/perfetto/trace_processor/trace_processor.h
@@ -22,7 +22,6 @@
#include <vector>
#include "perfetto/base/optional.h"
-#include "perfetto/base/string_view.h"
#include "perfetto/trace_processor/basic_types.h"
namespace perfetto {
@@ -107,7 +106,7 @@
// Executes a SQLite query on the loaded portion of the trace. The returned
// iterator can be used to load rows from the result.
- virtual Iterator ExecuteQuery(base::StringView sql) = 0;
+ virtual Iterator ExecuteQuery(const std::string& sql) = 0;
// Computes the given metrics on the loded portion of the trace. If
// successful, the output argument |metrics_proto| will be filled with the
diff --git a/src/perfetto_cmd/BUILD.gn b/src/perfetto_cmd/BUILD.gn
index 995989f..f7c7b3a 100644
--- a/src/perfetto_cmd/BUILD.gn
+++ b/src/perfetto_cmd/BUILD.gn
@@ -26,7 +26,7 @@
"../../protos/perfetto/config:lite",
"../base",
"../protozero",
- "../tracing:ipc_consumer",
+ "../tracing:ipc",
]
sources = [
"config.cc",
@@ -38,6 +38,8 @@
"perfetto_config.descriptor.h",
"rate_limiter.cc",
"rate_limiter.h",
+ "trigger_producer.cc",
+ "trigger_producer.h",
]
if (perfetto_build_with_android) {
deps += [ "../base:android_task_runner" ]
diff --git a/src/perfetto_cmd/perfetto_cmd.cc b/src/perfetto_cmd/perfetto_cmd.cc
index a461683..6ad30b1 100644
--- a/src/perfetto_cmd/perfetto_cmd.cc
+++ b/src/perfetto_cmd/perfetto_cmd.cc
@@ -43,6 +43,7 @@
#include "perfetto/tracing/core/trace_packet.h"
#include "src/perfetto_cmd/config.h"
#include "src/perfetto_cmd/pbtxt_to_pb.h"
+#include "src/perfetto_cmd/trigger_producer.h"
#include "perfetto/config/trace_config.pb.h"
@@ -131,13 +132,16 @@
int PerfettoCmd::PrintUsage(const char* argv0) {
PERFETTO_ELOG(R"(
Usage: %s
- --background -d : Exits immediately and continues tracing in background
- --config -c : /path/to/trace/config/file or - for stdin
- --out -o : /path/to/out/trace/file or - for stdout
- --dropbox TAG : Upload trace into DropBox using tag TAG
- --no-guardrails : Ignore guardrails triggered when using --dropbox (for testing).
- --txt : Parse config as pbtxt. Not a stable API. Not for production use.
- --reset-guardrails : Resets the state of the guardails and exits (for testing).
+ --background -d : Exits immediately and continues tracing in background
+ --config -c : /path/to/trace/config/file or - for stdin
+ --out -o : /path/to/out/trace/file or - for stdout
+ --dropbox TAG : Upload trace into DropBox using tag TAG
+ --no-guardrails : Ignore guardrails triggered when using --dropbox (for testing).
+ --txt : Parse config as pbtxt. Not a stable API. Not for production use.
+ --reset-guardrails : Resets the state of the guardails and exits (for testing).
+ --trigger NAME : Activate the NAME on to the service. If specified multiple times
+ will activate them all. Cannot be used with --config or
+ configuration flags.
--help -h
@@ -172,6 +176,7 @@
OPT_CONFIG_UID,
OPT_SUBSCRIPTION_ID,
OPT_RESET_GUARDRAILS,
+ OPT_TRIGGER,
OPT_PBTXT_CONFIG,
OPT_DROPBOX,
OPT_ATRACE_APP,
@@ -197,6 +202,7 @@
{"config-uid", required_argument, nullptr, OPT_CONFIG_UID},
{"subscription-id", required_argument, nullptr, OPT_SUBSCRIPTION_ID},
{"reset-guardrails", no_argument, nullptr, OPT_RESET_GUARDRAILS},
+ {"trigger", required_argument, nullptr, OPT_TRIGGER},
{"detach", required_argument, nullptr, OPT_DETACH},
{"attach", required_argument, nullptr, OPT_ATTACH},
{"is_detached", required_argument, nullptr, OPT_IS_DETACHED},
@@ -215,6 +221,7 @@
ConfigOptions config_options;
bool has_config_options = false;
+ std::vector<std::string> triggers_to_activate;
for (;;) {
int option =
@@ -304,6 +311,11 @@
return 0;
}
+ if (option == OPT_TRIGGER) {
+ triggers_to_activate.push_back(std::string(optarg));
+ continue;
+ }
+
if (option == OPT_ALERT_ID) {
statsd_metadata.set_triggering_alert_id(atoll(optarg));
continue;
@@ -381,8 +393,9 @@
// 1) A proto-encoded file/stdin (-c ...).
// 2) A proto-text file/stdin (-c ... --txt).
// 3) A set of option arguments (-t 10s -s 10m).
- // The only case in which a trace config is not expected is --attach. In this
- // case we are just re-attaching to an already started session.
+ // The only cases in which a trace config is not expected is --attach or
+ // --trigger. For both of these we are just acting on already
+ // existing sessions.
perfetto::protos::TraceConfig trace_config_proto;
bool parsed = false;
if (is_attach()) {
@@ -390,6 +403,15 @@
PERFETTO_ELOG("Cannot specify a trace config with --attach");
return 1;
}
+ if (!triggers_to_activate.empty()) {
+ PERFETTO_ELOG("Cannot specify triggers to activate with --attach");
+ return 1;
+ }
+ } else if (!triggers_to_activate.empty()) {
+ if (!trace_config_raw.empty() || has_config_options) {
+ PERFETTO_ELOG("Cannot specify a trace config with --trigger");
+ return 1;
+ }
} else if (has_config_options) {
if (!trace_config_raw.empty()) {
PERFETTO_ELOG(
@@ -403,7 +425,6 @@
PERFETTO_ELOG("The TraceConfig is empty");
return 1;
}
-
PERFETTO_DLOG("Parsing TraceConfig, %zu bytes", trace_config_raw.size());
if (parse_as_pbtxt) {
parsed = ParseTraceConfigPbtxt(config_file_name, trace_config_raw,
@@ -418,7 +439,7 @@
*trace_config_proto.mutable_statsd_metadata() = std::move(statsd_metadata);
trace_config_->FromProto(trace_config_proto);
trace_config_raw.clear();
- } else if (!is_attach()) {
+ } else if (!is_attach() && triggers_to_activate.empty()) {
PERFETTO_ELOG("The trace config is invalid, bailing out.");
return 1;
}
@@ -440,6 +461,8 @@
PERFETTO_ELOG("Can't pass an --out file (or --dropbox) to --attach");
return 1;
}
+ } else if (!triggers_to_activate.empty()) {
+ open_out_file = false;
} else if (trace_out_path_.empty() && dropbox_tag_.empty()) {
PERFETTO_ELOG("Either --out or --dropbox is required");
return 1;
@@ -478,6 +501,21 @@
}
}
+ // If we are just activating triggers then we don't need to rate limit,
+ // connect as a consumer or run the trace. So bail out after processing all
+ // the options.
+ if (!triggers_to_activate.empty()) {
+ bool finished_with_success = false;
+ TriggerProducer producer(&task_runner_,
+ [this, &finished_with_success](bool success) {
+ finished_with_success = success;
+ task_runner_.Quit();
+ },
+ &triggers_to_activate);
+ task_runner_.Run();
+ return finished_with_success ? 0 : 1;
+ }
+
RateLimiter::Args args{};
args.is_dropbox = !dropbox_tag_.empty();
args.current_time = base::GetWallTimeS();
@@ -589,6 +627,12 @@
}
} else {
#if PERFETTO_BUILDFLAG(PERFETTO_ANDROID_BUILD)
+ if (bytes_written_ == 0) {
+ PERFETTO_ILOG("Skipping upload to dropbox. Empty trace.");
+ did_process_full_trace_ = true;
+ task_runner_.Quit();
+ return;
+ }
android::sp<android::os::DropBoxManager> dropbox =
new android::os::DropBoxManager();
fseek(*trace_out_stream_, 0, SEEK_SET);
diff --git a/src/perfetto_cmd/trigger_producer.cc b/src/perfetto_cmd/trigger_producer.cc
new file mode 100644
index 0000000..45d6c4c
--- /dev/null
+++ b/src/perfetto_cmd/trigger_producer.cc
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#include "src/perfetto_cmd/trigger_producer.h"
+
+#include <memory>
+
+#include "perfetto/base/logging.h"
+#include "perfetto/tracing/core/producer.h"
+#include "perfetto/tracing/ipc/producer_ipc_client.h"
+#include "src/tracing/ipc/default_socket.h"
+
+namespace perfetto {
+
+class DataSourceConfig;
+
+TriggerProducer::TriggerProducer(base::TaskRunner* task_runner,
+ std::function<void(bool)> callback,
+ const std::vector<std::string>* const triggers)
+ : task_runner_(task_runner),
+ callback_(std::move(callback)),
+ triggers_(triggers),
+ producer_endpoint_(ProducerIPCClient::Connect(GetProducerSocket(),
+ this,
+ "perfetto_cmd_producer",
+ task_runner)),
+ weak_factory_(this) {
+ // Give the socket up to 10 seconds to attach and send the triggers before
+ // reporting a failure.
+ auto weak_this = weak_factory_.GetWeakPtr();
+ task_runner_->PostDelayedTask(
+ [weak_this]() {
+ if (!weak_this || weak_this->issued_callback_)
+ return;
+ weak_this->issued_callback_ = true;
+ weak_this->callback_(false);
+ },
+ 10000);
+}
+
+TriggerProducer::~TriggerProducer() {}
+
+void TriggerProducer::OnConnect() {
+ PERFETTO_DLOG("Producer connected, sending triggers.");
+ // Send activation signal.
+ producer_endpoint_->ActivateTriggers(*triggers_);
+ auto weak_this = weak_factory_.GetWeakPtr();
+ task_runner_->PostTask([weak_this]() {
+ if (!weak_this || weak_this->issued_callback_)
+ return;
+ weak_this->issued_callback_ = true;
+ weak_this->callback_(true);
+ });
+}
+
+void TriggerProducer::OnDisconnect() {}
+
+void TriggerProducer::OnTracingSetup() {}
+
+void TriggerProducer::SetupDataSource(DataSourceInstanceID,
+ const DataSourceConfig&) {
+ PERFETTO_DFATAL("Attempted to SetupDataSource() on commandline producer");
+}
+void TriggerProducer::StartDataSource(DataSourceInstanceID,
+ const DataSourceConfig&) {
+ PERFETTO_DFATAL("Attempted to StartDataSource() on commandline producer");
+}
+void TriggerProducer::StopDataSource(DataSourceInstanceID) {
+ PERFETTO_DFATAL("Attempted to StopDataSource() on commandline producer");
+}
+void TriggerProducer::Flush(FlushRequestID,
+ const DataSourceInstanceID*,
+ size_t) {
+ PERFETTO_DFATAL("Attempted to Flush() on commandline producer");
+}
+
+} // namespace perfetto
diff --git a/src/perfetto_cmd/trigger_producer.h b/src/perfetto_cmd/trigger_producer.h
new file mode 100644
index 0000000..dfd189c
--- /dev/null
+++ b/src/perfetto_cmd/trigger_producer.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#ifndef SRC_PERFETTO_CMD_TRIGGER_PRODUCER_H_
+#define SRC_PERFETTO_CMD_TRIGGER_PRODUCER_H_
+
+#include <string>
+#include <vector>
+
+#include "perfetto/base/task_runner.h"
+#include "perfetto/base/weak_ptr.h"
+#include "perfetto/tracing/core/producer.h"
+#include "perfetto/tracing/core/tracing_service.h"
+
+namespace perfetto {
+
+class DataSourceConfig;
+
+// This is a producer that only sends the provided |triggers| to the service. It
+// will never register any data sources.
+class TriggerProducer : public Producer {
+ public:
+ TriggerProducer(base::TaskRunner* task_runner,
+ std::function<void(bool)> callback,
+ const std::vector<std::string>* const triggers);
+ ~TriggerProducer() override;
+
+ // We will call Trigger() on the |producer_endpoint_| and then
+ // immediately call |callback_|.
+ void OnConnect() override;
+ // We have no clean up to do OnDisconnect.
+ void OnDisconnect() override;
+
+ // Unimplemented methods are below this.
+ void OnTracingSetup() override;
+ void SetupDataSource(DataSourceInstanceID, const DataSourceConfig&) override;
+ void StartDataSource(DataSourceInstanceID, const DataSourceConfig&) override;
+ void StopDataSource(DataSourceInstanceID) override;
+ void Flush(FlushRequestID, const DataSourceInstanceID*, size_t) override;
+
+ private:
+ bool issued_callback_ = false;
+ base::TaskRunner* const task_runner_;
+ const std::function<void(bool)> callback_;
+ const std::vector<std::string>* const triggers_;
+ std::unique_ptr<TracingService::ProducerEndpoint> producer_endpoint_;
+ base::WeakPtrFactory<TriggerProducer> weak_factory_;
+};
+
+} // namespace perfetto
+
+#endif // SRC_PERFETTO_CMD_TRIGGER_PRODUCER_H_
diff --git a/src/trace_processor/BUILD.gn b/src/trace_processor/BUILD.gn
index b60f291..dc489f9 100644
--- a/src/trace_processor/BUILD.gn
+++ b/src/trace_processor/BUILD.gn
@@ -235,7 +235,10 @@
"../protozero",
]
if (perfetto_build_standalone) {
- sources += [ "json_trace_utils_unittest.cc" ]
+ sources += [
+ "json_trace_tokenizer_unittest.cc",
+ "json_trace_utils_unittest.cc",
+ ]
deps += [ "../../gn:jsoncpp_deps" ]
}
}
diff --git a/src/trace_processor/json_trace_tokenizer.cc b/src/trace_processor/json_trace_tokenizer.cc
index ade28dc..f6e3de6 100644
--- a/src/trace_processor/json_trace_tokenizer.cc
+++ b/src/trace_processor/json_trace_tokenizer.cc
@@ -20,16 +20,13 @@
#include <json/value.h>
#include "src/trace_processor/json_trace_utils.h"
+#include "src/trace_processor/stats.h"
#include "src/trace_processor/trace_blob_view.h"
#include "src/trace_processor/trace_sorter.h"
namespace perfetto {
namespace trace_processor {
-namespace {
-
-enum ReadDictRes { kFoundDict, kNeedsMoreData, kEndOfTrace, kFatalError };
-
// Parses at most one JSON dictionary and returns a pointer to the end of it,
// or nullptr if no dict could be detected.
// This is to avoid decoding the full trace in memory and reduce heap traffic.
@@ -42,9 +39,23 @@
int braces = 0;
int square_brackets = 0;
const char* dict_begin = nullptr;
+ bool in_string = false;
+ bool is_escaping = false;
for (const char* s = start; s < end; s++) {
if (isspace(*s) || *s == ',')
continue;
+ if (*s == '"' && !is_escaping) {
+ in_string = !in_string;
+ continue;
+ }
+ if (in_string) {
+ // If we're in a string and we see a backslash and the last character was
+ // not a backslash the next character is escaped:
+ is_escaping = *s == '\\' && !is_escaping;
+ // If we're currently parsing a string we should ignore otherwise special
+ // characters:
+ continue;
+ }
if (*s == '{') {
if (braces == 0)
dict_begin = s;
@@ -79,14 +90,10 @@
}
square_brackets--;
}
-
- // TODO(primiano): skip braces in quoted strings, e.g.: {"foo": "ba{z" }
}
return kNeedsMoreData;
}
-} // namespace
-
JsonTraceTokenizer::JsonTraceTokenizer(TraceProcessorContext* ctx)
: context_(ctx) {}
JsonTraceTokenizer::~JsonTraceTokenizer() = default;
@@ -125,7 +132,10 @@
base::Optional<int64_t> opt_ts =
json_trace_utils::CoerceToNs((*value)["ts"]);
- PERFETTO_CHECK(opt_ts.has_value());
+ if (!opt_ts.has_value()) {
+ context_->storage->IncrementStats(stats::json_tokenizer_failure);
+ continue;
+ }
int64_t ts = opt_ts.value();
trace_sorter->PushJsonValue(ts, std::move(value));
diff --git a/src/trace_processor/json_trace_tokenizer.h b/src/trace_processor/json_trace_tokenizer.h
index 91d120c..c2c4ab5 100644
--- a/src/trace_processor/json_trace_tokenizer.h
+++ b/src/trace_processor/json_trace_tokenizer.h
@@ -32,6 +32,15 @@
class TraceProcessorContext;
+// Visible for testing.
+enum ReadDictRes { kFoundDict, kNeedsMoreData, kEndOfTrace, kFatalError };
+
+// Visible for testing.
+ReadDictRes ReadOneJsonDict(const char* start,
+ const char* end,
+ Json::Value* value,
+ const char** next);
+
// Reads a JSON trace in chunks and extracts top level json objects.
class JsonTraceTokenizer : public ChunkedTraceReader {
public:
diff --git a/src/trace_processor/json_trace_tokenizer_unittest.cc b/src/trace_processor/json_trace_tokenizer_unittest.cc
new file mode 100644
index 0000000..9bfb87e
--- /dev/null
+++ b/src/trace_processor/json_trace_tokenizer_unittest.cc
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#include "src/trace_processor/json_trace_tokenizer.h"
+
+#include <json/value.h>
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+namespace perfetto {
+namespace trace_processor {
+namespace {
+
+TEST(JsonTraceTokenizerTest, Success) {
+ const char* start = R"({ "foo": "bar" })";
+ const char* end = start + strlen(start);
+ const char* next = nullptr;
+ Json::Value value;
+ ReadDictRes result = ReadOneJsonDict(start, end, &value, &next);
+
+ ASSERT_EQ(result, kFoundDict);
+ ASSERT_EQ(next, end);
+ ASSERT_EQ(value["foo"].asString(), "bar");
+}
+
+TEST(JsonTraceTokenizerTest, QuotedBraces) {
+ const char* start = R"({ "foo": "}\"bar{\\" })";
+ const char* end = start + strlen(start);
+ const char* next = nullptr;
+ Json::Value value;
+ ReadDictRes result = ReadOneJsonDict(start, end, &value, &next);
+
+ ASSERT_EQ(result, kFoundDict);
+ ASSERT_EQ(next, end);
+ ASSERT_EQ(value["foo"].asString(), "}\"bar{\\");
+}
+
+TEST(JsonTraceTokenizerTest, TwoDicts) {
+ const char* start = R"({"foo": 1}, {"bar": 2})";
+ const char* middle = start + strlen(R"({"foo": 1})");
+ const char* end = start + strlen(start);
+ const char* next = nullptr;
+ Json::Value value;
+
+ ASSERT_EQ(ReadOneJsonDict(start, end, &value, &next), kFoundDict);
+ ASSERT_EQ(next, middle);
+ ASSERT_EQ(value["foo"].asInt(), 1);
+
+ ASSERT_EQ(ReadOneJsonDict(next, end, &value, &next), kFoundDict);
+ ASSERT_EQ(next, end);
+ ASSERT_EQ(value["bar"].asInt(), 2);
+}
+
+TEST(JsonTraceTokenizerTest, NeedMoreData) {
+ const char* start = R"({"foo": 1)";
+ const char* end = start + strlen(start);
+ const char* next = nullptr;
+ Json::Value value;
+
+ ASSERT_EQ(ReadOneJsonDict(start, end, &value, &next), kNeedsMoreData);
+ ASSERT_EQ(next, nullptr);
+}
+
+TEST(JsonTraceTokenizerTest, FatalError) {
+ const char* start = R"({helloworld})";
+ const char* end = start + strlen(start);
+ const char* next = nullptr;
+ Json::Value value;
+
+ ASSERT_EQ(ReadOneJsonDict(start, end, &value, &next), kFatalError);
+ ASSERT_EQ(next, nullptr);
+}
+
+} // namespace
+} // namespace trace_processor
+} // namespace perfetto
diff --git a/src/trace_processor/stats.h b/src/trace_processor/stats.h
index 2b59783..f75a3ad 100644
--- a/src/trace_processor/stats.h
+++ b/src/trace_processor/stats.h
@@ -88,7 +88,8 @@
F(traced_tracing_sessions, kSingle, kInfo, kTrace), \
F(vmstat_unknown_keys, kSingle, kError, kAnalysis), \
F(clock_sync_failure, kSingle, kError, kAnalysis), \
- F(process_tracker_errors, kSingle, kError, kAnalysis)
+ F(process_tracker_errors, kSingle, kError, kAnalysis), \
+ F(json_tokenizer_failure, kSingle, kError, kTrace)
// clang-format on
enum Type {
diff --git a/src/trace_processor/trace_processor_impl.cc b/src/trace_processor/trace_processor_impl.cc
index 36cf6b6..7dab3fb 100644
--- a/src/trace_processor/trace_processor_impl.cc
+++ b/src/trace_processor/trace_processor_impl.cc
@@ -363,9 +363,9 @@
}
TraceProcessor::Iterator TraceProcessorImpl::ExecuteQuery(
- base::StringView sql) {
+ const std::string& sql) {
sqlite3_stmt* raw_stmt;
- int err = sqlite3_prepare_v2(*db_, sql.data(), static_cast<int>(sql.size()),
+ int err = sqlite3_prepare_v2(*db_, sql.c_str(), static_cast<int>(sql.size()),
&raw_stmt, nullptr);
base::Optional<std::string> error;
uint32_t col_count = 0;
diff --git a/src/trace_processor/trace_processor_impl.h b/src/trace_processor/trace_processor_impl.h
index 4fcb919..db6ab22 100644
--- a/src/trace_processor/trace_processor_impl.h
+++ b/src/trace_processor/trace_processor_impl.h
@@ -62,7 +62,7 @@
const protos::RawQueryArgs&,
std::function<void(const protos::RawQueryResult&)>) override;
- Iterator ExecuteQuery(base::StringView sql) override;
+ Iterator ExecuteQuery(const std::string& sql) override;
int ComputeMetric(const std::vector<std::string>& metric_names,
std::vector<uint8_t>* metrics) override;
diff --git a/src/trace_processor/trace_processor_shell.cc b/src/trace_processor/trace_processor_shell.cc
index ef78526..1b3d2b6 100644
--- a/src/trace_processor/trace_processor_shell.cc
+++ b/src/trace_processor/trace_processor_shell.cc
@@ -203,7 +203,7 @@
std::string attach_sql =
"ATTACH DATABASE '" + output_name + "' AS perfetto_export";
- auto attach_it = g_tp->ExecuteQuery(base::StringView(attach_sql));
+ auto attach_it = g_tp->ExecuteQuery(attach_sql);
bool attach_has_more = attach_it.Next();
PERFETTO_DCHECK(!attach_has_more);
if (base::Optional<std::string> opt_error = attach_it.GetLastError()) {
@@ -220,7 +220,7 @@
std::string export_sql = "CREATE TABLE perfetto_export." + table_name +
" AS SELECT * FROM " + table_name;
- auto export_it = g_tp->ExecuteQuery(base::StringView(export_sql));
+ auto export_it = g_tp->ExecuteQuery(export_sql);
bool export_has_more = export_it.Next();
PERFETTO_DCHECK(!export_has_more);
if (base::Optional<std::string> opt_error = export_it.GetLastError()) {
@@ -349,16 +349,14 @@
}
void PrintQueryResultAsCsv(TraceProcessor::Iterator* it, FILE* output) {
- for (uint32_t rows = 0; it->Next(); rows++) {
- if (rows == 0) {
- for (uint32_t c = 0; c < it->ColumnCount(); c++) {
- if (c > 0)
- fprintf(output, ",");
- fprintf(output, "\"%s\"", it->GetColumName(c).c_str());
- }
- fprintf(output, "\n");
- }
+ for (uint32_t c = 0; c < it->ColumnCount(); c++) {
+ if (c > 0)
+ fprintf(output, ",");
+ fprintf(output, "\"%s\"", it->GetColumName(c).c_str());
+ }
+ fprintf(output, "\n");
+ for (uint32_t rows = 0; it->Next(); rows++) {
for (uint32_t c = 0; c < it->ColumnCount(); c++) {
if (c > 0)
fprintf(output, ",");
@@ -423,13 +421,19 @@
PERFETTO_ILOG("Executing query: %s", sql_query.c_str());
- auto it = g_tp->ExecuteQuery(base::StringView(sql_query));
+ auto it = g_tp->ExecuteQuery(sql_query);
if (base::Optional<std::string> opt_error = it.GetLastError()) {
PERFETTO_ELOG("SQLite error: %s", opt_error->c_str());
is_query_error = true;
break;
}
- if (has_output && it.ColumnCount() > 0) {
+ if (it.ColumnCount() == 0) {
+ bool it_has_more = it.Next();
+ PERFETTO_DCHECK(!it_has_more);
+ continue;
+ }
+
+ if (has_output) {
PERFETTO_ELOG(
"More than one query generated result rows. This is "
"unsupported.");
@@ -437,7 +441,7 @@
break;
}
PrintQueryResultAsCsv(&it, output);
- has_output = it.ColumnCount() > 0;
+ has_output = true;
}
return !is_query_error;
}
diff --git a/src/tracing/BUILD.gn b/src/tracing/BUILD.gn
index f697933..412ef9b 100644
--- a/src/tracing/BUILD.gn
+++ b/src/tracing/BUILD.gn
@@ -155,30 +155,6 @@
]
}
- # IPC transport: only consumer side
- # TODO(fmayer): Remove duplication between this and ipc.
- source_set("ipc_consumer") {
- public_deps = [
- "../../include/perfetto/tracing/core",
- "../../include/perfetto/tracing/ipc",
- ]
- sources = [
- "ipc/consumer/consumer_ipc_client_impl.cc",
- "ipc/consumer/consumer_ipc_client_impl.h",
- "ipc/default_socket.cc",
- "ipc/default_socket.h",
- "ipc/posix_shared_memory.cc",
- "ipc/posix_shared_memory.h",
- ]
- deps = [
- ":tracing",
- "../../gn:default_deps",
- "../../protos/perfetto/ipc",
- "../base",
- "../ipc",
- ]
- }
-
# Posix specialization of the tracing library for Linux/Android/Mac. Provides
# an IPC transport over a UNIX domain socket.
static_library("ipc") {
diff --git a/test/cts/producer/jni/fake_producer_jni.cc b/test/cts/producer/jni/fake_producer_jni.cc
index 412a0fb..cbeafe2 100644
--- a/test/cts/producer/jni/fake_producer_jni.cc
+++ b/test/cts/producer/jni/fake_producer_jni.cc
@@ -28,7 +28,7 @@
void ListenAndRespond(const std::string& name) {
base::TestTaskRunner task_runner;
FakeProducer producer(name);
- producer.Connect(GetProducerSocket(), &task_runner, [] {});
+ producer.Connect(GetProducerSocket(), &task_runner, [] {}, [] {});
task_runner.Run();
}
} // namespace
diff --git a/test/end_to_end_integrationtest.cc b/test/end_to_end_integrationtest.cc
index e1017d5..8d3b3cc 100644
--- a/test/end_to_end_integrationtest.cc
+++ b/test/end_to_end_integrationtest.cc
@@ -26,8 +26,10 @@
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "perfetto/base/build_config.h"
+#include "perfetto/base/file_utils.h"
#include "perfetto/base/logging.h"
#include "perfetto/base/pipe.h"
+#include "perfetto/base/temp_file.h"
#include "perfetto/traced/traced.h"
#include "perfetto/tracing/core/trace_config.h"
#include "perfetto/tracing/core/trace_packet.h"
@@ -39,6 +41,7 @@
#include "test/task_runner_thread_delegates.h"
#include "test/test_helper.h"
+#include "perfetto/trace/trace.pb.h"
#include "perfetto/trace/trace_packet.pb.h"
#include "perfetto/trace/trace_packet.pbzero.h"
@@ -73,7 +76,15 @@
class PerfettoCmdlineTest : public ::testing::Test {
public:
- void SetUp() override { test_helper_.StartServiceIfRequired(); }
+ void SetUp() override {
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
+ // On android P perfetto shell only has permission to write traces to this
+ // directory. So we update TMPDIR to this so that our client will have
+ // permissions.
+ setenv("TMPDIR", "/data/misc/perfetto-traces", 1);
+#endif
+ test_helper_.StartServiceIfRequired();
+ }
void TearDown() override {}
@@ -118,8 +129,13 @@
#if PERFETTO_BUILDFLAG(PERFETTO_START_DAEMONS)
setenv("PERFETTO_CONSUMER_SOCK_NAME", TestHelper::GetConsumerSocketName(),
1);
+ setenv("PERFETTO_PRODUCER_SOCK_NAME", TestHelper::GetProducerSocketName(),
+ 1);
_exit(PerfettoCmdMain(static_cast<int>(argv.size() - 1), argv.data()));
#else
+ // We have to choose a location that the perfetto binary will have
+ // permission to write to. This does not include /data/local/tmp so
+ // instead we override TMPDIR to the trace directory.
execv("/system/bin/perfetto", &argv[0]);
_exit(3);
#endif
@@ -592,4 +608,218 @@
EXPECT_EQ(0, Exec({"--attach=valid_stop", "--stop"}));
}
+TEST_F(PerfettoCmdlineTest, NoSanitizers(StartTracingTrigger)) {
+ // See |message_count| and |message_size| in the TraceConfig above.
+ constexpr size_t kMessageCount = 11;
+ constexpr size_t kMessageSize = 32;
+ protos::TraceConfig trace_config;
+ trace_config.add_buffers()->set_size_kb(1024);
+ auto* ds_config = trace_config.add_data_sources()->mutable_config();
+ ds_config->set_name("android.perfetto.FakeProducer");
+ ds_config->mutable_for_testing()->set_message_count(kMessageCount);
+ ds_config->mutable_for_testing()->set_message_size(kMessageSize);
+ auto* trigger_cfg = trace_config.mutable_trigger_config();
+ trigger_cfg->set_trigger_mode(
+ protos::TraceConfig::TriggerConfig::START_TRACING);
+ trigger_cfg->set_trigger_timeout_ms(15000);
+ auto* trigger = trigger_cfg->add_triggers();
+ trigger->set_name("trigger_name");
+ // |stop_delay_ms| must be long enough that we can write the packets in
+ // before the trace finishes. This has to be long enough for the slowest
+ // emulator. But as short as possible to prevent the test running a long
+ // time.
+ trigger->set_stop_delay_ms(500);
+
+ // We have 5 normal preample packets (trace config, clock, system info, sync
+ // marker, stats) and then since this is a trace with a trigger config we have
+ // an additional ReceivedTriggers packet.
+ constexpr size_t kPreamblePackets = 6;
+
+ base::TestTaskRunner task_runner;
+
+ // Enable tracing and detach as soon as it gets started.
+ TestHelper helper(&task_runner);
+ helper.StartServiceIfRequired();
+ auto* fake_producer = helper.ConnectFakeProducer();
+ EXPECT_TRUE(fake_producer);
+ base::TempFile trace_output = base::TempFile::Create();
+ const std::string path = trace_output.path();
+ trace_output.Unlink();
+ std::thread background_trace([&path, &trace_config, this]() {
+ EXPECT_EQ(0, Exec(
+ {
+ "-o", path, "-c", "-",
+ },
+ trace_config.SerializeAsString()));
+ });
+
+ helper.WaitForProducerSetup();
+ EXPECT_EQ(0, Exec({"--trigger=trigger_name"})) << "stderr: " << stderr_;
+
+ // Wait for the producer to start, and then write out 11 packets.
+ helper.WaitForProducerEnabled();
+ auto on_data_written = task_runner.CreateCheckpoint("data_written");
+ fake_producer->ProduceEventBatch(helper.WrapTask(on_data_written));
+ task_runner.RunUntilCheckpoint("data_written");
+ background_trace.join();
+
+ std::string trace_str;
+ base::ReadFile(path, &trace_str);
+ protos::Trace trace;
+ ASSERT_TRUE(trace.ParseFromString(trace_str));
+ EXPECT_EQ(kPreamblePackets + kMessageCount, trace.packet_size());
+ for (const auto& packet : trace.packet()) {
+ if (packet.data_case() == protos::TracePacket::kTraceConfig) {
+ // Ensure the trace config properly includes the trigger mode we set.
+ EXPECT_EQ(protos::TraceConfig::TriggerConfig::START_TRACING,
+ packet.trace_config().trigger_config().trigger_mode());
+ } else if (packet.data_case() == protos::TracePacket::kTrigger) {
+ // validate that the triggers are properly added to the trace.
+ EXPECT_EQ("trigger_name", packet.trigger().trigger_name());
+ } else if (packet.data_case() == protos::TracePacket::kForTesting) {
+ // Make sure that the data size is correctly set based on what we
+ // requested.
+ EXPECT_EQ(kMessageSize, packet.for_testing().str().size());
+ }
+ }
+}
+
+TEST_F(PerfettoCmdlineTest, NoSanitizers(StopTracingTrigger)) {
+ // See |message_count| and |message_size| in the TraceConfig above.
+ constexpr size_t kMessageCount = 11;
+ constexpr size_t kMessageSize = 32;
+ protos::TraceConfig trace_config;
+ trace_config.add_buffers()->set_size_kb(1024);
+ auto* ds_config = trace_config.add_data_sources()->mutable_config();
+ ds_config->set_name("android.perfetto.FakeProducer");
+ ds_config->mutable_for_testing()->set_message_count(kMessageCount);
+ ds_config->mutable_for_testing()->set_message_size(kMessageSize);
+ auto* trigger_cfg = trace_config.mutable_trigger_config();
+ trigger_cfg->set_trigger_mode(
+ protos::TraceConfig::TriggerConfig::STOP_TRACING);
+ trigger_cfg->set_trigger_timeout_ms(15000);
+ auto* trigger = trigger_cfg->add_triggers();
+ trigger->set_name("trigger_name");
+ // |stop_delay_ms| must be long enough that we can write the packets in
+ // before the trace finishes. This has to be long enough for the slowest
+ // emulator. But as short as possible to prevent the test running a long
+ // time.
+ trigger->set_stop_delay_ms(500);
+ trigger = trigger_cfg->add_triggers();
+ trigger->set_name("trigger_name_3");
+ trigger->set_stop_delay_ms(60000);
+
+ // We have 5 normal preample packets (trace config, clock, system info, sync
+ // marker, stats) and then since this is a trace with a trigger config we have
+ // an additional ReceivedTriggers packet.
+ constexpr size_t kPreamblePackets = 7;
+
+ base::TestTaskRunner task_runner;
+
+ // Enable tracing and detach as soon as it gets started.
+ TestHelper helper(&task_runner);
+ helper.StartServiceIfRequired();
+ auto* fake_producer = helper.ConnectFakeProducer();
+ EXPECT_TRUE(fake_producer);
+
+ base::TempFile trace_output = base::TempFile::Create();
+ const std::string path = trace_output.path();
+ trace_output.Unlink();
+ std::thread background_trace([&path, &trace_config, this]() {
+ EXPECT_EQ(0, Exec(
+ {
+ "-o", path, "-c", "-",
+ },
+ trace_config.SerializeAsString()));
+ });
+
+ helper.WaitForProducerEnabled();
+ // Wait for the producer to start, and then write out 11 packets, before the
+ // trace actually starts (the trigger is seen).
+ auto on_data_written = task_runner.CreateCheckpoint("data_written_1");
+ fake_producer->ProduceEventBatch(helper.WrapTask(on_data_written));
+ task_runner.RunUntilCheckpoint("data_written_1");
+
+ EXPECT_EQ(0, Exec({"--trigger=trigger_name_2", "--trigger=trigger_name",
+ "--trigger=trigger_name_3"}))
+ << "stderr: " << stderr_;
+
+ background_trace.join();
+
+ std::string trace_str;
+ base::ReadFile(path, &trace_str);
+ protos::Trace trace;
+ ASSERT_TRUE(trace.ParseFromString(trace_str));
+ EXPECT_EQ(kPreamblePackets + kMessageCount, trace.packet_size());
+ bool seen_first_trigger = false;
+ for (const auto& packet : trace.packet()) {
+ if (packet.data_case() == protos::TracePacket::kTraceConfig) {
+ // Ensure the trace config properly includes the trigger mode we set.
+ EXPECT_EQ(protos::TraceConfig::TriggerConfig::STOP_TRACING,
+ packet.trace_config().trigger_config().trigger_mode());
+ } else if (packet.data_case() == protos::TracePacket::kTrigger) {
+ // validate that the triggers are properly added to the trace.
+ if (!seen_first_trigger) {
+ EXPECT_EQ("trigger_name", packet.trigger().trigger_name());
+ seen_first_trigger = true;
+ } else {
+ EXPECT_EQ("trigger_name_3", packet.trigger().trigger_name());
+ }
+ } else if (packet.data_case() == protos::TracePacket::kForTesting) {
+ // Make sure that the data size is correctly set based on what we
+ // requested.
+ EXPECT_EQ(kMessageSize, packet.for_testing().str().size());
+ }
+ }
+}
+
+// Dropbox on the commandline client only works on android builds. So disable
+// this test on all other builds.
+#if PERFETTO_BUILDFLAG(PERFETTO_ANDROID_BUILD)
+TEST_F(PerfettoCmdlineTest, NoSanitizers(NoDataNoFileWithoutTrigger)) {
+#else
+TEST_F(PerfettoCmdlineTest, DISABLED_NoDataNoFileWithoutTrigger) {
+#endif
+ // See |message_count| and |message_size| in the TraceConfig above.
+ constexpr size_t kMessageCount = 11;
+ constexpr size_t kMessageSize = 32;
+ protos::TraceConfig trace_config;
+ trace_config.add_buffers()->set_size_kb(1024);
+ auto* ds_config = trace_config.add_data_sources()->mutable_config();
+ ds_config->set_name("android.perfetto.FakeProducer");
+ ds_config->mutable_for_testing()->set_message_count(kMessageCount);
+ ds_config->mutable_for_testing()->set_message_size(kMessageSize);
+ auto* trigger_cfg = trace_config.mutable_trigger_config();
+ trigger_cfg->set_trigger_mode(
+ protos::TraceConfig::TriggerConfig::STOP_TRACING);
+ trigger_cfg->set_trigger_timeout_ms(1000);
+ auto* trigger = trigger_cfg->add_triggers();
+ trigger->set_name("trigger_name");
+ // |stop_delay_ms| must be long enough that we can write the packets in
+ // before the trace finishes. This has to be long enough for the slowest
+ // emulator. But as short as possible to prevent the test running a long
+ // time.
+ trigger->set_stop_delay_ms(500);
+ trigger = trigger_cfg->add_triggers();
+
+ // Enable tracing and detach as soon as it gets started.
+ base::TestTaskRunner task_runner;
+ TestHelper helper(&task_runner);
+ helper.StartServiceIfRequired();
+ auto* fake_producer = helper.ConnectFakeProducer();
+ EXPECT_TRUE(fake_producer);
+
+ std::thread background_trace([&trace_config, this]() {
+ EXPECT_EQ(0, Exec(
+ {
+ "--dropbox", "TAG", "--no-guardrails", "-c", "-",
+ },
+ trace_config.SerializeAsString()));
+ });
+ background_trace.join();
+
+ EXPECT_THAT(stderr_,
+ ::testing::HasSubstr("Skipping upload to dropbox. Empty trace."));
+}
+
} // namespace perfetto
diff --git a/test/fake_producer.cc b/test/fake_producer.cc
index 20c4049..0ae52a1 100644
--- a/test/fake_producer.cc
+++ b/test/fake_producer.cc
@@ -37,11 +37,13 @@
void FakeProducer::Connect(
const char* socket_name,
base::TaskRunner* task_runner,
+ std::function<void()> on_setup_data_source_instance,
std::function<void()> on_create_data_source_instance) {
PERFETTO_DCHECK_THREAD(thread_checker_);
task_runner_ = task_runner;
endpoint_ = ProducerIPCClient::Connect(
socket_name, this, "android.perfetto.FakeProducer", task_runner);
+ on_setup_data_source_instance_ = std::move(on_setup_data_source_instance);
on_create_data_source_instance_ = std::move(on_create_data_source_instance);
}
@@ -58,7 +60,9 @@
}
void FakeProducer::SetupDataSource(DataSourceInstanceID,
- const DataSourceConfig&) {}
+ const DataSourceConfig&) {
+ task_runner_->PostTask(on_setup_data_source_instance_);
+}
void FakeProducer::StartDataSource(DataSourceInstanceID,
const DataSourceConfig& source_config) {
diff --git a/test/fake_producer.h b/test/fake_producer.h
index baacb9f..5e8d587 100644
--- a/test/fake_producer.h
+++ b/test/fake_producer.h
@@ -37,6 +37,7 @@
void Connect(const char* socket_name,
base::TaskRunner* task_runner,
+ std::function<void()> on_setup_data_source_instance,
std::function<void()> on_create_data_source_instance);
// Produces a batch of events (as configured in the DataSourceConfig) and
@@ -64,6 +65,7 @@
uint32_t message_size_ = 0;
uint32_t message_count_ = 0;
uint32_t max_messages_per_second_ = 0;
+ std::function<void()> on_setup_data_source_instance_;
std::function<void()> on_create_data_source_instance_;
std::unique_ptr<TracingService::ProducerEndpoint> endpoint_;
std::unique_ptr<TraceWriter> trace_writer_;
diff --git a/test/task_runner_thread_delegates.h b/test/task_runner_thread_delegates.h
index 241ecc4..8eecbba 100644
--- a/test/task_runner_thread_delegates.h
+++ b/test/task_runner_thread_delegates.h
@@ -64,14 +64,17 @@
class FakeProducerDelegate : public ThreadDelegate {
public:
FakeProducerDelegate(const std::string& producer_socket,
+ std::function<void()> setup_callback,
std::function<void()> connect_callback)
: producer_socket_(producer_socket),
+ setup_callback_(std::move(setup_callback)),
connect_callback_(std::move(connect_callback)) {}
~FakeProducerDelegate() override = default;
void Initialize(base::TaskRunner* task_runner) override {
producer_.reset(new FakeProducer("android.perfetto.FakeProducer"));
producer_->Connect(producer_socket_.c_str(), task_runner,
+ std::move(setup_callback_),
std::move(connect_callback_));
}
@@ -80,6 +83,7 @@
private:
std::string producer_socket_;
std::unique_ptr<FakeProducer> producer_;
+ std::function<void()> setup_callback_;
std::function<void()> connect_callback_;
};
} // namespace perfetto
diff --git a/test/test_helper.cc b/test/test_helper.cc
index 2d5b7e2..bcacda3 100644
--- a/test/test_helper.cc
+++ b/test/test_helper.cc
@@ -88,6 +88,7 @@
FakeProducer* TestHelper::ConnectFakeProducer() {
std::unique_ptr<FakeProducerDelegate> producer_delegate(
new FakeProducerDelegate(TEST_PRODUCER_SOCK_NAME,
+ WrapTask(CreateCheckpoint("producer.setup")),
WrapTask(CreateCheckpoint("producer.enabled"))));
FakeProducerDelegate* producer_delegate_cached = producer_delegate.get();
producer_thread_.Start(std::move(producer_delegate));
@@ -150,6 +151,10 @@
RunUntilCheckpoint("consumer.connected." + std::to_string(cur_consumer_num_));
}
+void TestHelper::WaitForProducerSetup() {
+ RunUntilCheckpoint("producer.setup");
+}
+
void TestHelper::WaitForProducerEnabled() {
RunUntilCheckpoint("producer.enabled");
}
@@ -186,4 +191,9 @@
return TEST_CONSUMER_SOCK_NAME;
}
+// static
+const char* TestHelper::GetProducerSocketName() {
+ return TEST_PRODUCER_SOCK_NAME;
+}
+
} // namespace perfetto
diff --git a/test/test_helper.h b/test/test_helper.h
index 083a529..8b2ae2d 100644
--- a/test/test_helper.h
+++ b/test/test_helper.h
@@ -33,6 +33,7 @@
class TestHelper : public Consumer {
public:
static const char* GetConsumerSocketName();
+ static const char* GetProducerSocketName();
explicit TestHelper(base::TestTaskRunner* task_runner);
@@ -58,6 +59,7 @@
bool AttachConsumer(const std::string& key);
void WaitForConsumerConnect();
+ void WaitForProducerSetup();
void WaitForProducerEnabled();
void WaitForTracingDisabled(uint32_t timeout_ms = 5000);
void WaitForReadData(uint32_t read_count = 0);
diff --git a/test/trace_processor/memory_counters_args_string_filter_null.out b/test/trace_processor/memory_counters_args_string_filter_null.out
index e69de29..683ea38 100644
--- a/test/trace_processor/memory_counters_args_string_filter_null.out
+++ b/test/trace_processor/memory_counters_args_string_filter_null.out
@@ -0,0 +1 @@
+"string_value"
diff --git a/test/trace_processor/sfgate_smoke.out b/test/trace_processor/sfgate_smoke.out
index e69de29..72fd6a8 100644
--- a/test/trace_processor/sfgate_smoke.out
+++ b/test/trace_processor/sfgate_smoke.out
@@ -0,0 +1 @@
+"ts","cpu","dur","ts_end","utid","end_state","priority","row_id"
diff --git a/tools/heap_profile b/tools/heap_profile
index 3ef51f2..0e29f86 100755
--- a/tools/heap_profile
+++ b/tools/heap_profile
@@ -37,6 +37,13 @@
TRACE_TO_TEXT_BASE_URL = (
'https://storage.googleapis.com/perfetto/')
+NULL = open(os.devnull)
+NOOUT = {
+ 'stdout': NULL,
+ 'stderr': NULL,
+}
+
+
def check_hash(file_name, sha_value):
with open(file_name, 'rb') as fd:
# TODO(fmayer): Chunking.
@@ -64,8 +71,6 @@
return local_file
-NULL = open('/dev/null', 'r')
-
CFG_IDENT = ' '
CFG='''buffers {{
size_kb: 32768
@@ -203,10 +208,14 @@
old_handler = signal.signal(signal.SIGINT, sigint_handler)
print("Profiling active. Press Ctrl+C to terminate.")
+ print("You may disconnect your device.")
exists = True
- while exists and not IS_INTERRUPTED:
+ device_connected = True
+ while not device_connected or (exists and not IS_INTERRUPTED):
exists = subprocess.call(
- ['adb', 'shell', '[ -d /proc/{} ]'.format(perfetto_pid)]) == 0
+ ['adb', 'shell', '[ -d /proc/{} ]'.format(perfetto_pid)],
+ **NOOUT) == 0
+ device_connected = subprocess.call(['adb', 'shell', 'true'], **NOOUT) == 0
time.sleep(1)
signal.signal(signal.SIGINT, old_handler)
if IS_INTERRUPTED:
@@ -223,8 +232,7 @@
'/data/misc/perfetto-traces/profile-{}'.format(user),
'/tmp/profile'], stdout=NULL)
trace_to_text_output = subprocess.check_output(
- [trace_to_text_binary, 'profile', '/tmp/profile'],
- stderr=NULL)
+ [trace_to_text_binary, 'profile', '/tmp/profile'])
profile_path = None
for word in trace_to_text_output.split():
if 'heap_profile-' in word:
diff --git a/tools/trace_to_text/trace_to_systrace.cc b/tools/trace_to_text/trace_to_systrace.cc
index 276e47d..5e07c05 100644
--- a/tools/trace_to_text/trace_to_systrace.cc
+++ b/tools/trace_to_text/trace_to_systrace.cc
@@ -130,7 +130,7 @@
output_(output) {}
template <typename Callback>
- bool RunQuery(base::StringView sql, Callback callback) {
+ bool RunQuery(const std::string& sql, Callback callback) {
char buffer[2048];
auto iterator = tp_->ExecuteQuery(sql);
for (uint32_t rows = 0; iterator.Next(); rows++) {