heap profilers: reject data sources with unparsable cmdlines in cfgs

For logical parity with aosp/1254587. Same argument - fail fast on
invalid configs, instead of trying to degrade (and deal with associated
edge cases).

I don't think this does anything in practice at the time of writing.
NormalizeCmdLine() should always succeed unless there's a bug in the
caller (only error case is not being null-terminated afaict).

Test: profiling by cmdline not obviously broken, tried:
      tools/heap_profile -i 4096 --name system_server -d 10000

Change-Id: I1658fe9bc7c07b5210e6c219a6976f6e8e40246f
diff --git a/src/profiling/common/proc_utils.cc b/src/profiling/common/proc_utils.cc
index b65a956..d69826e 100644
--- a/src/profiling/common/proc_utils.cc
+++ b/src/profiling/common/proc_utils.cc
@@ -21,6 +21,7 @@
 #include <unistd.h>
 
 #include "perfetto/ext/base/file_utils.h"
+#include "perfetto/ext/base/optional.h"
 #include "perfetto/profiling/normalize.h"
 
 namespace perfetto {
@@ -41,24 +42,25 @@
 
 }  // namespace
 
-std::vector<std::string> NormalizeCmdlines(
+base::Optional<std::vector<std::string>> NormalizeCmdlines(
     const std::vector<std::string>& cmdlines) {
   std::vector<std::string> normalized_cmdlines;
-  for (std::string cmdline : cmdlines) {
+  normalized_cmdlines.reserve(cmdlines.size());
+
+  for (size_t i = 0; i < cmdlines.size(); i++) {
+    std::string cmdline = cmdlines[i];  // mutable copy
     // Add nullbyte to make sure it's a C string.
     cmdline.resize(cmdline.size() + 1, '\0');
-    std::string normalized;
     char* cmdline_cstr = &(cmdline[0]);
     ssize_t size = NormalizeCmdLine(&cmdline_cstr, cmdline.size());
     if (size == -1) {
-      PERFETTO_PLOG("Failed to normalize cmdline %s. Skipping.",
-                    cmdline.c_str());
-      continue;
+      PERFETTO_PLOG("Failed to normalize cmdline %s. Stopping the parse.",
+                    cmdlines[i].c_str());
+      return base::nullopt;
     }
-    normalized_cmdlines.emplace_back(
-        std::string(cmdline_cstr, static_cast<size_t>(size)));
+    normalized_cmdlines.emplace_back(cmdline_cstr, static_cast<size_t>(size));
   }
-  return normalized_cmdlines;
+  return base::make_optional(normalized_cmdlines);
 }
 
 // This is mostly the same as GetHeapprofdProgramProperty in
diff --git a/src/profiling/common/proc_utils.h b/src/profiling/common/proc_utils.h
index 11a0785..045bf69 100644
--- a/src/profiling/common/proc_utils.h
+++ b/src/profiling/common/proc_utils.h
@@ -21,6 +21,7 @@
 #include <set>
 #include <vector>
 
+#include "perfetto/ext/base/optional.h"
 #include "perfetto/ext/base/scoped_file.h"
 
 namespace perfetto {
@@ -43,7 +44,7 @@
   }
 }
 
-std::vector<std::string> NormalizeCmdlines(
+base::Optional<std::vector<std::string>> NormalizeCmdlines(
     const std::vector<std::string>& cmdlines);
 
 void FindAllProfilablePids(std::set<pid_t>* pids);
diff --git a/src/profiling/memory/heapprofd_producer.cc b/src/profiling/memory/heapprofd_producer.cc
index 98c11c7..be277e8 100644
--- a/src/profiling/memory/heapprofd_producer.cc
+++ b/src/profiling/memory/heapprofd_producer.cc
@@ -25,6 +25,7 @@
 #include <unistd.h>
 
 #include "perfetto/ext/base/file_utils.h"
+#include "perfetto/ext/base/optional.h"
 #include "perfetto/ext/base/string_utils.h"
 #include "perfetto/ext/base/thread_task_runner.h"
 #include "perfetto/ext/tracing/core/trace_writer.h"
@@ -313,14 +314,18 @@
     return;
   }
 
-  std::vector<std::string> normalized_cmdlines =
+  base::Optional<std::vector<std::string>> normalized_cmdlines =
       NormalizeCmdlines(heapprofd_config.process_cmdline());
+  if (!normalized_cmdlines.has_value()) {
+    PERFETTO_ELOG("Rejecting data source due to invalid cmdline in config.");
+    return;
+  }
 
   // Child mode is only interested in the first data source matching the
   // already-connected process.
   if (mode_ == HeapprofdMode::kChild) {
     if (!ConfigTargetsProcess(heapprofd_config, target_process_,
-                              normalized_cmdlines)) {
+                              normalized_cmdlines.value())) {
       PERFETTO_DLOG("Child mode skipping setup of unrelated data source.");
       return;
     }
@@ -354,7 +359,7 @@
       heapprofd_config.block_client_timeout_us();
   cli_config.enable_extra_guardrails = ds_config.enable_extra_guardrails();
   data_source.config = heapprofd_config;
-  data_source.normalized_cmdlines = std::move(normalized_cmdlines);
+  data_source.normalized_cmdlines = std::move(normalized_cmdlines.value());
   data_source.stop_timeout_ms = ds_config.stop_timeout_ms();
 
   InterningOutputTracker::WriteFixedInterningsPacket(
diff --git a/src/profiling/memory/java_hprof_producer.cc b/src/profiling/memory/java_hprof_producer.cc
index cfaf633..6422238 100644
--- a/src/profiling/memory/java_hprof_producer.cc
+++ b/src/profiling/memory/java_hprof_producer.cc
@@ -18,6 +18,7 @@
 
 #include <signal.h>
 
+#include "perfetto/ext/base/optional.h"
 #include "perfetto/ext/tracing/core/trace_writer.h"
 #include "src/profiling/common/proc_utils.h"
 
@@ -82,8 +83,13 @@
   ds.id = id;
   for (uint64_t pid : config.pid())
     ds.pids.emplace(static_cast<pid_t>(pid));
-  auto normalized_cmdlines = NormalizeCmdlines(config.process_cmdline());
-  FindPidsForCmdlines(normalized_cmdlines, &ds.pids);
+  base::Optional<std::vector<std::string>> normalized_cmdlines =
+      NormalizeCmdlines(config.process_cmdline());
+  if (!normalized_cmdlines.has_value()) {
+    PERFETTO_ELOG("Rejecting data source due to invalid cmdline in config.");
+    return;
+  }
+  FindPidsForCmdlines(normalized_cmdlines.value(), &ds.pids);
   ds.config = std::move(config);
   data_sources_.emplace(id, std::move(ds));
 }