Merge "[tp] Do not treat terminated process as having data loss"
diff --git a/Android.bp b/Android.bp
index d21b05c..5b76850 100644
--- a/Android.bp
+++ b/Android.bp
@@ -9480,6 +9480,7 @@
"src/trace_processor/sqlite/sql_stats_table.cc",
"src/trace_processor/sqlite/sqlite3_str_split.cc",
"src/trace_processor/sqlite/sqlite_raw_table.cc",
+ "src/trace_processor/sqlite/sqlite_utils.cc",
"src/trace_processor/sqlite/stats_table.cc",
"src/trace_processor/sqlite/window_operator_table.cc",
],
diff --git a/BUILD b/BUILD
index 4de70f0..3ab8ffb 100644
--- a/BUILD
+++ b/BUILD
@@ -1457,6 +1457,7 @@
"src/trace_processor/sqlite/sqlite3_str_split.h",
"src/trace_processor/sqlite/sqlite_raw_table.cc",
"src/trace_processor/sqlite/sqlite_raw_table.h",
+ "src/trace_processor/sqlite/sqlite_utils.cc",
"src/trace_processor/sqlite/sqlite_utils.h",
"src/trace_processor/sqlite/stats_table.cc",
"src/trace_processor/sqlite/stats_table.h",
diff --git a/include/perfetto/ext/base/file_utils.h b/include/perfetto/ext/base/file_utils.h
index 64e4126..7c7b8e0 100644
--- a/include/perfetto/ext/base/file_utils.h
+++ b/include/perfetto/ext/base/file_utils.h
@@ -64,6 +64,7 @@
ScopedFile OpenFile(const std::string& path,
int flags,
FileOpenMode = kFileModeInvalid);
+ScopedFstream OpenFstream(const char* path, const char* mode);
// This is an alias for close(). It's to avoid leaking Windows.h in headers.
// Exported because ScopedFile is used in the /include/ext API by Chromium
diff --git a/include/perfetto/trace_processor/basic_types.h b/include/perfetto/trace_processor/basic_types.h
index 1ca649a..7cfff9d 100644
--- a/include/perfetto/trace_processor/basic_types.h
+++ b/include/perfetto/trace_processor/basic_types.h
@@ -127,6 +127,10 @@
//
// The flag has no impact on non-proto traces.
bool analyze_trace_proto_content = false;
+
+ // When set to true, trace processor will be augmented with a bunch of helpful
+ // features for local development such as extra SQL fuctions.
+ bool enable_dev_features = false;
};
// Represents a dynamically typed value returned by SQL.
diff --git a/include/perfetto/tracing/traced_value.h b/include/perfetto/tracing/traced_value.h
index 5a43e51..366db4b 100644
--- a/include/perfetto/tracing/traced_value.h
+++ b/include/perfetto/tracing/traced_value.h
@@ -177,9 +177,9 @@
static TracedValue CreateFromProto(protos::pbzero::DebugAnnotation* proto,
EventContext* event_context = nullptr);
- inline explicit TracedValue(protos::pbzero::DebugAnnotation* annotation,
- EventContext* event_context,
- internal::CheckedScope* parent_scope)
+ inline TracedValue(protos::pbzero::DebugAnnotation* annotation,
+ EventContext* event_context,
+ internal::CheckedScope* parent_scope)
: annotation_(annotation),
event_context_(event_context),
checked_scope_(parent_scope) {}
@@ -228,9 +228,9 @@
private:
friend class TracedValue;
- inline explicit TracedArray(protos::pbzero::DebugAnnotation* annotation,
- EventContext* event_context,
- internal::CheckedScope* parent_scope)
+ inline TracedArray(protos::pbzero::DebugAnnotation* annotation,
+ EventContext* event_context,
+ internal::CheckedScope* parent_scope)
: annotation_(annotation),
event_context_(event_context),
checked_scope_(parent_scope) {}
@@ -286,7 +286,7 @@
// Create a |TracedDictionary| which will populate the given field of the
// given |message|.
template <typename MessageType, typename FieldMetadata>
- inline explicit TracedDictionary(
+ inline TracedDictionary(
MessageType* message,
protozero::proto_utils::internal::FieldMetadataHelper<FieldMetadata>,
EventContext* event_context,
diff --git a/src/base/file_utils.cc b/src/base/file_utils.cc
index ce4584c..945350f 100644
--- a/src/base/file_utils.cc
+++ b/src/base/file_utils.cc
@@ -28,6 +28,7 @@
#include "perfetto/base/logging.h"
#include "perfetto/base/platform_handle.h"
#include "perfetto/base/status.h"
+#include "perfetto/ext/base/optional.h"
#include "perfetto/ext/base/scoped_file.h"
#include "perfetto/ext/base/utils.h"
@@ -35,6 +36,7 @@
#include <Windows.h>
#include <direct.h>
#include <io.h>
+#include <stringapiset.h>
#else
#include <dirent.h>
#include <unistd.h>
@@ -50,6 +52,26 @@
int CloseFindHandle(HANDLE h) {
return FindClose(h) ? 0 : -1;
}
+
+Optional<std::wstring> ToUtf16(const std::string str) {
+ int len = MultiByteToWideChar(CP_UTF8, 0, str.data(),
+ static_cast<int>(str.size()), nullptr, 0);
+ if (len < 0) {
+ return base::nullopt;
+ }
+ std::vector<wchar_t> tmp;
+ tmp.resize(static_cast<std::vector<wchar_t>::size_type>(len));
+ len =
+ MultiByteToWideChar(CP_UTF8, 0, str.data(), static_cast<int>(str.size()),
+ tmp.data(), static_cast<int>(tmp.size()));
+ if (len < 0) {
+ return base::nullopt;
+ }
+ PERFETTO_CHECK(static_cast<std::vector<wchar_t>::size_type>(len) ==
+ tmp.size());
+ return std::wstring(tmp.data(), tmp.size());
+}
+
#endif
} // namespace
@@ -202,6 +224,23 @@
return fd;
}
+ScopedFstream OpenFstream(const char* path, const char* mode) {
+ ScopedFstream file;
+// On Windows fopen interprets filename using the ANSI or OEM codepage but
+// sqlite3_value_text returns a UTF-8 string. To make sure we interpret the
+// filename correctly we use _wfopen and a UTF-16 string on windows.
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+ auto w_path = ToUtf16(path);
+ auto w_mode = ToUtf16(mode);
+ if (w_path && w_mode) {
+ file.reset(_wfopen(w_path->c_str(), w_mode->c_str()));
+ }
+#else
+ file.reset(fopen(path, mode));
+#endif
+ return file;
+}
+
bool FileExists(const std::string& path) {
#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
return _access(path.c_str(), 0) == 0;
diff --git a/src/trace_processor/metrics/sql/BUILD.gn b/src/trace_processor/metrics/sql/BUILD.gn
index a7cfa42..c326a07 100644
--- a/src/trace_processor/metrics/sql/BUILD.gn
+++ b/src/trace_processor/metrics/sql/BUILD.gn
@@ -155,7 +155,6 @@
"chrome/rail_modes.sql",
"chrome/scroll_flow_event_queuing_delay.sql",
"chrome/scroll_flow_event.sql",
- "chrome/event_latency_to_breakdowns.sql",
"chrome/scroll_jank_cause_blocking_task.sql",
"chrome/scroll_jank_cause_blocking_touch_move.sql",
"chrome/scroll_jank_cause_get_bitmap.sql",
diff --git a/src/trace_processor/sqlite/BUILD.gn b/src/trace_processor/sqlite/BUILD.gn
index f44e2c6..d294b7d 100644
--- a/src/trace_processor/sqlite/BUILD.gn
+++ b/src/trace_processor/sqlite/BUILD.gn
@@ -38,6 +38,7 @@
"sqlite3_str_split.h",
"sqlite_raw_table.cc",
"sqlite_raw_table.h",
+ "sqlite_utils.cc",
"sqlite_utils.h",
"stats_table.cc",
"stats_table.h",
diff --git a/src/trace_processor/sqlite/sqlite_utils.cc b/src/trace_processor/sqlite/sqlite_utils.cc
new file mode 100644
index 0000000..9be14ea
--- /dev/null
+++ b/src/trace_processor/sqlite/sqlite_utils.cc
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2022 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/sqlite/sqlite_utils.h"
+
+namespace perfetto {
+namespace trace_processor {
+namespace sqlite_utils {
+
+std::wstring SqliteValueToWString(sqlite3_value* value) {
+ PERFETTO_CHECK(sqlite3_value_type(value) == SQLITE_TEXT);
+ int len = sqlite3_value_bytes16(value);
+ PERFETTO_CHECK(len >= 0);
+ size_t count = static_cast<size_t>(len) / sizeof(wchar_t);
+ return std::wstring(
+ reinterpret_cast<const wchar_t*>(sqlite3_value_text16(value)), count);
+}
+
+} // namespace sqlite_utils
+} // namespace trace_processor
+} // namespace perfetto
diff --git a/src/trace_processor/sqlite/sqlite_utils.h b/src/trace_processor/sqlite/sqlite_utils.h
index 0266cc5..90a98bc 100644
--- a/src/trace_processor/sqlite/sqlite_utils.h
+++ b/src/trace_processor/sqlite/sqlite_utils.h
@@ -19,6 +19,7 @@
#include <math.h>
#include <sqlite3.h>
+#include <string>
#include "perfetto/base/status.h"
#include "perfetto/ext/base/string_utils.h"
@@ -233,6 +234,10 @@
return base::OkStatus();
}
+// Reads a `SQLITE_TEXT` value and returns it as a wstring (UTF-16) in the
+// default byte order. `value` must be a `SQLITE_TEXT`.
+std::wstring SqliteValueToWString(sqlite3_value* value);
+
} // namespace sqlite_utils
} // namespace trace_processor
} // namespace perfetto
diff --git a/src/trace_processor/trace_processor_impl.cc b/src/trace_processor/trace_processor_impl.cc
index b783644..caa8d21 100644
--- a/src/trace_processor/trace_processor_impl.cc
+++ b/src/trace_processor/trace_processor_impl.cc
@@ -23,6 +23,8 @@
#include "perfetto/base/status.h"
#include "perfetto/base/time.h"
#include "perfetto/ext/base/base64.h"
+#include "perfetto/ext/base/file_utils.h"
+#include "perfetto/ext/base/scoped_file.h"
#include "perfetto/ext/base/string_splitter.h"
#include "perfetto/ext/base/string_utils.h"
#include "perfetto/ext/trace_processor/demangle.h"
@@ -51,6 +53,7 @@
#include "src/trace_processor/importers/systrace/systrace_trace_parser.h"
#include "src/trace_processor/iterator_impl.h"
#include "src/trace_processor/sqlite/create_function.h"
+#include "src/trace_processor/sqlite/create_function_internal.h"
#include "src/trace_processor/sqlite/create_view_function.h"
#include "src/trace_processor/sqlite/pprof_functions.h"
#include "src/trace_processor/sqlite/register_function.h"
@@ -329,10 +332,10 @@
sqlite3_value** argv,
SqlValue& /*out*/,
Destructors&) {
- FILE* output;
+ base::ScopedFstream output;
if (sqlite3_value_type(argv[0]) == SQLITE_INTEGER) {
// Assume input is an FD.
- output = fdopen(sqlite3_value_int(argv[0]), "w");
+ output.reset(fdopen(sqlite3_value_int(argv[0]), "w"));
if (!output) {
return base::ErrStatus(
"EXPORT_JSON: Couldn't open output file from given FD");
@@ -340,12 +343,12 @@
} else {
const char* filename =
reinterpret_cast<const char*>(sqlite3_value_text(argv[0]));
- output = fopen(filename, "w");
+ output = base::OpenFstream(filename, "w");
if (!output) {
return base::ErrStatus("EXPORT_JSON: Couldn't open output file");
}
}
- return json::ExportJson(storage, output);
+ return json::ExportJson(storage, output.get());
}
struct Hash : public SqlFunction {
@@ -451,6 +454,61 @@
return base::OkStatus();
}
+struct WriteFile : public SqlFunction {
+ using Context = TraceStorage;
+ static base::Status Run(TraceStorage* storage,
+ size_t,
+ sqlite3_value** argv,
+ SqlValue&,
+ Destructors&);
+};
+
+base::Status WriteFile::Run(TraceStorage*,
+ size_t argc,
+ sqlite3_value** argv,
+ SqlValue& out,
+ Destructors&) {
+ if (argc != 2) {
+ return base::ErrStatus("WRITE_FILE: expected %d args but got %zu", 2, argc);
+ }
+
+ base::Status status = TypeCheckSqliteValue(argv[0], SqlValue::kString);
+ if (!status.ok()) {
+ return base::ErrStatus("WRITE_FILE: argument 1, filename; %s",
+ status.c_message());
+ }
+
+ status = TypeCheckSqliteValue(argv[1], SqlValue::kBytes);
+ if (!status.ok()) {
+ return base::ErrStatus("WRITE_FILE: argument 2, content; %s",
+ status.c_message());
+ }
+
+ const std::string filename =
+ reinterpret_cast<const char*>(sqlite3_value_text(argv[0]));
+
+ base::ScopedFstream file = base::OpenFstream(filename.c_str(), "wb");
+ if (!file) {
+ return base::ErrStatus("WRITE_FILE: Couldn't open output file %s (%s)",
+ filename.c_str(), strerror(errno));
+ }
+
+ int int_len = sqlite3_value_bytes(argv[1]);
+ PERFETTO_CHECK(int_len >= 0);
+ size_t len = (static_cast<size_t>(int_len));
+ // Make sure to call last as sqlite3_value_bytes can invalidate pointer
+ // returned.
+ const void* data = sqlite3_value_text(argv[1]);
+ if (fwrite(data, 1, len, file.get()) != len || fflush(file.get()) != 0) {
+ return base::ErrStatus("WRITE_FILE: Failed to write to file %s (%s)",
+ filename.c_str(), strerror(errno));
+ }
+
+ out = SqlValue::Long(int_len);
+
+ return util::OkStatus();
+}
+
void LastNonNullStep(sqlite3_context* ctx, int argc, sqlite3_value** argv) {
if (argc != 1) {
sqlite3_result_error(
@@ -966,6 +1024,11 @@
PERFETTO_FATAL("For GCC");
}
+// Register SQL functions only used in local development instances.
+void RegisterDevFunctions(sqlite3* db) {
+ RegisterFunction<WriteFile>(db, "WRITE_FILE", 2);
+}
+
} // namespace
template <typename View>
@@ -1003,6 +1066,9 @@
db_.reset(std::move(db));
// New style function registration.
+ if (cfg.enable_dev_features) {
+ RegisterDevFunctions(db);
+ }
RegisterFunction<Glob>(db, "glob", 2);
RegisterFunction<Hash>(db, "HASH", -1);
RegisterFunction<Base64Encode>(db, "BASE64_ENCODE", 1);
diff --git a/src/trace_processor/trace_processor_shell.cc b/src/trace_processor/trace_processor_shell.cc
index 3998b78..caee3f7 100644
--- a/src/trace_processor/trace_processor_shell.cc
+++ b/src/trace_processor/trace_processor_shell.cc
@@ -1311,6 +1311,10 @@
config.skip_builtin_metric_paths.push_back(extension.virtual_path());
}
+ if (options.dev) {
+ config.enable_dev_features = true;
+ }
+
std::unique_ptr<TraceProcessor> tp = TraceProcessor::CreateInstance(config);
g_tp = tp.get();