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();