Update local symbolizer to support Windows.

Bug: 152017262
Change-Id: I0db88410f5a72c20f95f91913573dd2665294824
diff --git a/Android.bp b/Android.bp
index 7af5002..f13c5c6 100644
--- a/Android.bp
+++ b/Android.bp
@@ -6614,7 +6614,10 @@
 filegroup {
   name: "perfetto_src_profiling_symbolizer_symbolizer",
   srcs: [
+    "src/profiling/symbolizer/filesystem_posix.cc",
     "src/profiling/symbolizer/local_symbolizer.cc",
+    "src/profiling/symbolizer/scoped_read_mmap_posix.cc",
+    "src/profiling/symbolizer/subprocess_posix.cc",
     "src/profiling/symbolizer/symbolizer.cc",
   ],
 }
diff --git a/BUILD b/BUILD
index b84be89..cd7447d 100644
--- a/BUILD
+++ b/BUILD
@@ -663,8 +663,14 @@
 filegroup(
     name = "src_profiling_symbolizer_symbolizer",
     srcs = [
+        "src/profiling/symbolizer/filesystem.h",
+        "src/profiling/symbolizer/filesystem_posix.cc",
         "src/profiling/symbolizer/local_symbolizer.cc",
         "src/profiling/symbolizer/local_symbolizer.h",
+        "src/profiling/symbolizer/scoped_read_mmap.h",
+        "src/profiling/symbolizer/scoped_read_mmap_posix.cc",
+        "src/profiling/symbolizer/subprocess.h",
+        "src/profiling/symbolizer/subprocess_posix.cc",
         "src/profiling/symbolizer/symbolizer.cc",
         "src/profiling/symbolizer/symbolizer.h",
     ],
diff --git a/gn/BUILD.gn b/gn/BUILD.gn
index d782e9f..8093071 100644
--- a/gn/BUILD.gn
+++ b/gn/BUILD.gn
@@ -55,7 +55,8 @@
   if (enable_perfetto_tools) {
     perfetto_local_symbolizer =
         "PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_LINUX() || " +
-        "PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_MAC()"
+        "PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_MAC() ||" +
+        "PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_WIN()"
   } else {
     perfetto_local_symbolizer = "0"
   }
diff --git a/include/perfetto/base/build_configs/android_tree/perfetto_build_flags.h b/include/perfetto/base/build_configs/android_tree/perfetto_build_flags.h
index a150204..80d1ec5 100644
--- a/include/perfetto/base/build_configs/android_tree/perfetto_build_flags.h
+++ b/include/perfetto/base/build_configs/android_tree/perfetto_build_flags.h
@@ -36,7 +36,7 @@
 #define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_TP_LINENOISE() (0)
 #define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_TP_HTTPD() (0)
 #define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_TP_JSON() (0)
-#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_LOCAL_SYMBOLIZER() (PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_LINUX() || PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_MAC())
+#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_LOCAL_SYMBOLIZER() (PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_LINUX() || PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_MAC() ||PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_WIN())
 #define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_ZLIB() (1)
 
 // clang-format on
diff --git a/include/perfetto/base/build_configs/bazel/perfetto_build_flags.h b/include/perfetto/base/build_configs/bazel/perfetto_build_flags.h
index e4391e6..fc8f06a 100644
--- a/include/perfetto/base/build_configs/bazel/perfetto_build_flags.h
+++ b/include/perfetto/base/build_configs/bazel/perfetto_build_flags.h
@@ -36,7 +36,7 @@
 #define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_TP_LINENOISE() (1)
 #define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_TP_HTTPD() (PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_ANDROID() || PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_LINUX() || PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_MAC())
 #define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_TP_JSON() (1)
-#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_LOCAL_SYMBOLIZER() (PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_LINUX() || PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_MAC())
+#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_LOCAL_SYMBOLIZER() (PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_LINUX() || PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_MAC() ||PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_WIN())
 #define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_ZLIB() (1)
 
 // clang-format on
diff --git a/src/profiling/symbolizer/BUILD.gn b/src/profiling/symbolizer/BUILD.gn
index 79c770e..2129cb3 100644
--- a/src/profiling/symbolizer/BUILD.gn
+++ b/src/profiling/symbolizer/BUILD.gn
@@ -24,6 +24,26 @@
     "symbolizer.cc",
     "symbolizer.h",
   ]
+  if (is_linux || is_mac || is_android) {
+    sources += [
+      "filesystem.h",
+      "filesystem_posix.cc",
+      "scoped_read_mmap.h",
+      "scoped_read_mmap_posix.cc",
+      "subprocess.h",
+      "subprocess_posix.cc",
+    ]
+  }
+  if (is_win) {
+    sources += [
+      "filesystem.h",
+      "filesystem_windows.cc",
+      "scoped_read_mmap.h",
+      "scoped_read_mmap_windows.cc",
+      "subprocess.h",
+      "subprocess_windows.cc",
+    ]
+  }
 }
 
 source_set("symbolize_database") {
@@ -50,6 +70,7 @@
     ":symbolizer",
     "../../../gn:default_deps",
     "../../../gn:gtest_and_gmock",
+    "../../base:test_support",
   ]
   sources = [ "local_symbolizer_unittest.cc" ]
 }
diff --git a/src/profiling/symbolizer/filesystem.h b/src/profiling/symbolizer/filesystem.h
new file mode 100644
index 0000000..d9cf87c
--- /dev/null
+++ b/src/profiling/symbolizer/filesystem.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2020 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_PROFILING_SYMBOLIZER_FILESYSTEM_H_
+#define SRC_PROFILING_SYMBOLIZER_FILESYSTEM_H_
+
+#include "src/profiling/symbolizer/local_symbolizer.h"
+
+namespace perfetto {
+namespace profiling {
+
+using FileCallback = std::function<void(const char*, size_t)>;
+size_t GetFileSize(const std::string& file_path);
+bool WalkDirectories(std::vector<std::string> dirs, FileCallback fn);
+
+}  // namespace profiling
+}  // namespace perfetto
+
+#endif  // SRC_PROFILING_SYMBOLIZER_FILESYSTEM_H_
diff --git a/src/profiling/symbolizer/filesystem_posix.cc b/src/profiling/symbolizer/filesystem_posix.cc
new file mode 100644
index 0000000..d8c6e12
--- /dev/null
+++ b/src/profiling/symbolizer/filesystem_posix.cc
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2020 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/profiling/symbolizer/filesystem.h"
+
+#if PERFETTO_BUILDFLAG(PERFETTO_LOCAL_SYMBOLIZER)
+#include <fts.h>
+#include <sys/stat.h>
+#endif
+namespace perfetto {
+namespace profiling {
+#if PERFETTO_BUILDFLAG(PERFETTO_LOCAL_SYMBOLIZER)
+bool WalkDirectories(std::vector<std::string> dirs, FileCallback fn) {
+  std::vector<char*> dir_cstrs;
+  for (std::string& dir : dirs)
+    dir_cstrs.emplace_back(&dir[0]);
+  dir_cstrs.push_back(nullptr);
+  base::ScopedResource<FTS*, fts_close, nullptr> fts(
+      fts_open(&dir_cstrs[0], FTS_LOGICAL | FTS_NOCHDIR, nullptr));
+  if (!fts) {
+    PERFETTO_PLOG("fts_open");
+    return false;
+  }
+  FTSENT* ent;
+  while ((ent = fts_read(*fts))) {
+    if (ent->fts_info & FTS_F)
+      fn(ent->fts_path, static_cast<size_t>(ent->fts_statp->st_size));
+  }
+  return true;
+}
+
+size_t GetFileSize(const std::string& file_path) {
+  base::ScopedFile fd(base::OpenFile(file_path, O_RDONLY | O_CLOEXEC));
+  if (!fd) {
+    PERFETTO_PLOG("Failed to get file size %s", file_path.c_str());
+    return 0;
+  }
+  struct stat buf;
+  if (fstat(*fd, &buf) == -1) {
+    return 0;
+  }
+  return static_cast<size_t>(buf.st_size);
+}
+#else
+bool WalkDirectories(std::vector<std::string>, FileCallback) {
+  return false;
+}
+size_t GetFileSize(const std::string&) {
+  return 0;
+}
+#endif
+
+}  // namespace profiling
+}  // namespace perfetto
diff --git a/src/profiling/symbolizer/filesystem_windows.cc b/src/profiling/symbolizer/filesystem_windows.cc
new file mode 100644
index 0000000..3da70e7
--- /dev/null
+++ b/src/profiling/symbolizer/filesystem_windows.cc
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2020 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/profiling/symbolizer/filesystem.h"
+
+#define WIN32_MEAN_AND_LEAN
+#include <Windows.h>
+
+namespace perfetto {
+namespace profiling {
+
+bool WalkDirectories(std::vector<std::string> dirs, FileCallback fn) {
+  std::vector<std::string> sub_dirs;
+  for (const std::string& dir : dirs) {
+    WIN32_FIND_DATA file;
+    HANDLE fh = FindFirstFile((dir + "\\*").c_str(), &file);
+    if (fh != INVALID_HANDLE_VALUE) {
+      do {
+        std::string file_path = dir + "\\" + file.cFileName;
+        if (file.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
+          if (strcmp(file.cFileName, ".") != 0 &&
+              strcmp(file.cFileName, "..") != 0) {
+            sub_dirs.push_back(file_path);
+          }
+        } else {
+          ULARGE_INTEGER size;
+          size.HighPart = file.nFileSizeHigh;
+          size.LowPart = file.nFileSizeLow;
+          fn(file_path.c_str(), size.QuadPart);
+        }
+      } while (FindNextFile(fh, &file));
+    }
+    CloseHandle(fh);
+  }
+  if (!sub_dirs.empty()) {
+    WalkDirectories(sub_dirs, fn);
+  }
+  return true;
+}
+
+size_t GetFileSize(const std::string& file_path) {
+  HANDLE file = CreateFileA(file_path.c_str(), GENERIC_READ, FILE_SHARE_READ,
+                            NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+  if (file == INVALID_HANDLE_VALUE) {
+    PERFETTO_PLOG("Failed to get file size %s", file_path.c_str());
+    return 0;
+  }
+  LARGE_INTEGER file_size;
+  file_size.QuadPart = 0;
+  if (!GetFileSizeEx(file, &file_size)) {
+    PERFETTO_PLOG("Failed to get file size %s", file_path.c_str());
+  }
+  CloseHandle(file);
+  return file_size.QuadPart;
+}
+
+}  // namespace profiling
+}  // namespace perfetto
\ No newline at end of file
diff --git a/src/profiling/symbolizer/local_symbolizer.cc b/src/profiling/symbolizer/local_symbolizer.cc
index f930447..01639bc 100644
--- a/src/profiling/symbolizer/local_symbolizer.cc
+++ b/src/profiling/symbolizer/local_symbolizer.cc
@@ -20,6 +20,7 @@
 #include <fcntl.h>
 
 #include <memory>
+#include <sstream>
 #include <string>
 #include <vector>
 
@@ -28,6 +29,8 @@
 #include "perfetto/base/logging.h"
 #include "perfetto/ext/base/optional.h"
 #include "perfetto/ext/base/scoped_file.h"
+#include "src/profiling/symbolizer/filesystem.h"
+#include "src/profiling/symbolizer/scoped_read_mmap.h"
 
 namespace perfetto {
 namespace profiling {
@@ -61,46 +64,55 @@
 // Most of this translation unit is built only on Linux and MacOS. See
 // //gn/BUILD.gn.
 #if PERFETTO_BUILDFLAG(PERFETTO_LOCAL_SYMBOLIZER)
-
 #include "perfetto/ext/base/string_splitter.h"
 #include "perfetto/ext/base/string_utils.h"
 #include "perfetto/ext/base/utils.h"
 
-#include <fts.h>
 #include <inttypes.h>
 #include <signal.h>
-#include <sys/mman.h>
 #include <sys/stat.h>
 #include <sys/types.h>
-#include <sys/wait.h>
-#include <unistd.h>
+
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+#define F_OK 0
+#endif
 
 namespace perfetto {
 namespace profiling {
 
-namespace {
-
-std::vector<std::string> GetLines(FILE* f) {
+std::vector<std::string> GetLines(
+    std::function<int64_t(char*, size_t)> fn_read) {
   std::vector<std::string> lines;
-  size_t n = 0;
-  char* line = nullptr;
-  ssize_t rd = 0;
-  do {
-    rd = getline(&line, &n, f);
-    // Do not read empty line that terminates the output.
-    if (rd > 1) {
-      // Remove newline character.
-      PERFETTO_DCHECK(line[rd - 1] == '\n');
-      line[rd - 1] = '\0';
-      lines.emplace_back(line);
+  char buffer[512];
+  int64_t rd = 0;
+  // Cache the partial line of the previous read.
+  std::string last_line;
+  while ((rd = fn_read(buffer, sizeof(buffer))) > 0) {
+    std::string data(buffer, static_cast<size_t>(rd));
+    // Create stream buffer of last partial line + new data
+    std::stringstream stream(last_line + data);
+    std::string line;
+    last_line = "";
+    while (std::getline(stream, line)) {
+      // Return from reading when we read an empty line.
+      if (line.empty()) {
+        return lines;
+      } else if (stream.eof()) {
+        // Cache off the partial line when we hit end of stream.
+        last_line += line;
+        break;
+      } else {
+        lines.push_back(line);
+      }
     }
-    free(line);
-    line = nullptr;
-    n = 0;
-  } while (rd > 1);
+  }
+  if (rd == -1) {
+    PERFETTO_ELOG("Failed to read data from subprocess.");
+  }
   return lines;
 }
 
+namespace {
 // We cannot just include elf.h, as that only exists on Linux, and we want to
 // allow symbolization on other platforms as well. As we only need a small
 // subset, it is easiest to define the constants and structs ourselves.
@@ -315,27 +327,6 @@
   return base::nullopt;
 }
 
-class ScopedMmap {
- public:
-  ScopedMmap(void* addr,
-             size_t length,
-             int prot,
-             int flags,
-             int fd,
-             off_t offset)
-      : length_(length), ptr_(mmap(addr, length, prot, flags, fd, offset)) {}
-  ~ScopedMmap() {
-    if (ptr_ != MAP_FAILED)
-      munmap(ptr_, length_);
-  }
-
-  void* operator*() { return ptr_; }
-
- private:
-  size_t length_;
-  void* ptr_;
-};
-
 std::string SplitBuildID(const std::string& hex_build_id) {
   if (hex_build_id.size() < 3) {
     PERFETTO_DFATAL_OR_ELOG("Invalid build-id (< 3 char) %s",
@@ -358,17 +349,13 @@
   uint64_t load_bias;
 };
 
-base::Optional<BuildIdAndLoadBias> GetBuildIdAndLoadBias(
-    int fd,
-    const struct stat* statbuf) {
-  size_t size = static_cast<size_t>(statbuf->st_size);
-
+base::Optional<BuildIdAndLoadBias> GetBuildIdAndLoadBias(const char* fname,
+                                                         size_t size) {
   static_assert(EI_CLASS > EI_MAG3, "mem[EI_MAG?] accesses are in range.");
   if (size <= EI_CLASS)
     return base::nullopt;
-
-  ScopedMmap map(nullptr, size, PROT_READ, MAP_PRIVATE, fd, 0);
-  if (*map == MAP_FAILED) {
+  ScopedReadMmap map(fname, size);
+  if (!map.IsValid()) {
     PERFETTO_PLOG("mmap");
     return base::nullopt;
   }
@@ -397,57 +384,36 @@
   return base::nullopt;
 }
 
-template <typename F>
-bool WalkDirectories(std::vector<std::string> dirs, F fn) {
-  std::vector<char*> dir_cstrs;
-  for (std::string& dir : dirs)
-    dir_cstrs.emplace_back(&dir[0]);
-  dir_cstrs.push_back(nullptr);
-  base::ScopedResource<FTS*, fts_close, nullptr> fts(
-      fts_open(&dir_cstrs[0], FTS_LOGICAL | FTS_NOCHDIR, nullptr));
-  if (!fts) {
-    PERFETTO_PLOG("fts_open");
-    return false;
-  }
-  FTSENT* ent;
-  while ((ent = fts_read(*fts))) {
-    if (ent->fts_info & FTS_F)
-      fn(ent->fts_path, ent->fts_statp);
-  }
-  return true;
-}
-
 std::map<std::string, FoundBinary> BuildIdIndex(std::vector<std::string> dirs) {
   std::map<std::string, FoundBinary> result;
-  WalkDirectories(
-      std::move(dirs), [&result](const char* fname, const struct stat* stat) {
-        char magic[EI_MAG3 + 1];
-        auto fd = base::OpenFile(fname, O_RDONLY | O_CLOEXEC);
-        if (!fd) {
-          PERFETTO_PLOG("Failed to open %s", fname);
-          return;
-        }
-        ssize_t rd = PERFETTO_EINTR(read(*fd, &magic, sizeof(magic)));
-        if (rd == -1) {
-          PERFETTO_PLOG("Failed to read %s", fname);
-          return;
-        }
-        if (!IsElf(magic, static_cast<size_t>(rd))) {
-          PERFETTO_DLOG("%s not an ELF.", fname);
-          return;
-        }
-        if (lseek(*fd, 0, SEEK_SET) == -1) {
-          PERFETTO_PLOG("Failed to seek %s", fname);
-          return;
-        }
-        base::Optional<BuildIdAndLoadBias> build_id_and_load_bias =
-            GetBuildIdAndLoadBias(*fd, stat);
-
-        if (build_id_and_load_bias) {
-          result.emplace(build_id_and_load_bias->build_id,
-                         FoundBinary{fname, build_id_and_load_bias->load_bias});
-        }
-      });
+  WalkDirectories(std::move(dirs), [&result](const char* fname, size_t size) {
+    char magic[EI_MAG3 + 1];
+    // Scope file access. On windows OpenFile opens an exclusive lock.
+    // This lock needs to be released before mapping the file.
+    {
+      base::ScopedFile fd(base::OpenFile(fname, O_RDONLY));
+      if (!fd) {
+        PERFETTO_PLOG("Failed to open %s", fname);
+        return;
+      }
+      ssize_t rd = static_cast<ssize_t>(
+          PERFETTO_EINTR(read(*fd, &magic, sizeof(magic))));
+      if (rd != sizeof(magic)) {
+        PERFETTO_PLOG("Failed to read %s", fname);
+        return;
+      }
+      if (!IsElf(magic, static_cast<size_t>(rd))) {
+        PERFETTO_DLOG("%s not an ELF.", fname);
+        return;
+      }
+    }
+    base::Optional<BuildIdAndLoadBias> build_id_and_load_bias =
+        GetBuildIdAndLoadBias(fname, size);
+    if (build_id_and_load_bias) {
+      result.emplace(build_id_and_load_bias->build_id,
+                     FoundBinary{fname, build_id_and_load_bias->load_bias});
+    }
+  });
   return result;
 }
 
@@ -518,15 +484,15 @@
   if (access(symbol_file.c_str(), F_OK) != 0) {
     return base::nullopt;
   }
-  base::ScopedFile fd(base::OpenFile(symbol_file, O_RDONLY));
-  if (!fd)
+  // Openfile opens the file with an exclusive lock on windows.
+  size_t size = GetFileSize(symbol_file);
+
+  if (size == 0) {
     return base::nullopt;
-  struct stat statbuf;
-  if (fstat(*fd, &statbuf) == -1)
-    return base::nullopt;
+  }
 
   base::Optional<BuildIdAndLoadBias> build_id_and_load_bias =
-      GetBuildIdAndLoadBias(*fd, &statbuf);
+      GetBuildIdAndLoadBias(symbol_file.c_str(), size);
   if (!build_id_and_load_bias)
     return base::nullopt;
   if (build_id_and_load_bias->build_id != build_id) {
@@ -618,50 +584,29 @@
 
 LocalBinaryFinder::~LocalBinaryFinder() = default;
 
-Subprocess::Subprocess(const std::string& file, std::vector<std::string> args)
-    : input_pipe_(base::Pipe::Create(base::Pipe::kBothBlock)),
-      output_pipe_(base::Pipe::Create(base::Pipe::kBothBlock)) {
-  std::vector<char*> c_str_args(args.size() + 1, nullptr);
-  for (std::string& arg : args)
-    c_str_args.push_back(&(arg[0]));
-
-  if ((pid_ = fork()) == 0) {
-    // Child
-    PERFETTO_CHECK(dup2(*input_pipe_.rd, STDIN_FILENO) != -1);
-    PERFETTO_CHECK(dup2(*output_pipe_.wr, STDOUT_FILENO) != -1);
-    input_pipe_.wr.reset();
-    output_pipe_.rd.reset();
-    if (execvp(file.c_str(), &(c_str_args[0])) == -1)
-      PERFETTO_FATAL("Failed to exec %s", file.c_str());
-  }
-  PERFETTO_CHECK(pid_ != -1);
-  input_pipe_.rd.reset();
-  output_pipe_.wr.reset();
-}
-
-Subprocess::~Subprocess() {
-  if (pid_ != -1) {
-    kill(pid_, SIGKILL);
-    int wstatus;
-    PERFETTO_EINTR(waitpid(pid_, &wstatus, 0));
-  }
-}
-
 LLVMSymbolizerProcess::LLVMSymbolizerProcess()
-    : subprocess_("llvm-symbolizer", {"llvm-symbolizer"}),
-      read_file_(fdopen(subprocess_.read_fd(), "r")) {}
+    :
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+      subprocess_("llvm-symbolizer.exe", {}) {
+}
+#else
+      subprocess_("llvm-symbolizer", {"llvm-symbolizer"}) {
+}
+#endif
 
 std::vector<SymbolizedFrame> LLVMSymbolizerProcess::Symbolize(
     const std::string& binary,
     uint64_t address) {
   std::vector<SymbolizedFrame> result;
-
-  if (PERFETTO_EINTR(dprintf(subprocess_.write_fd(), "%s 0x%" PRIx64 "\n",
-                             binary.c_str(), address)) < 0) {
+  char buffer[1024];
+  int size = sprintf(buffer, "%s 0x%" PRIx64 "\n", binary.c_str(), address);
+  if (subprocess_.Write(buffer, static_cast<size_t>(size)) < 0) {
     PERFETTO_ELOG("Failed to write to llvm-symbolizer.");
     return result;
   }
-  auto lines = GetLines(read_file_);
+  auto lines = GetLines([&](char* read_buffer, size_t buffer_size) {
+    return subprocess_.Read(read_buffer, buffer_size);
+  });
   // llvm-symbolizer writes out records in the form of
   // Foo(Bar*)
   // foo.cc:123
diff --git a/src/profiling/symbolizer/local_symbolizer.h b/src/profiling/symbolizer/local_symbolizer.h
index 756a327..ae403b9 100644
--- a/src/profiling/symbolizer/local_symbolizer.h
+++ b/src/profiling/symbolizer/local_symbolizer.h
@@ -23,17 +23,18 @@
 #include <vector>
 
 #include "perfetto/ext/base/optional.h"
-#include "perfetto/ext/base/pipe.h"
+#include "perfetto/ext/base/scoped_file.h"
+#include "src/profiling/symbolizer/subprocess.h"
 #include "src/profiling/symbolizer/symbolizer.h"
 
 namespace perfetto {
 namespace profiling {
 
-#if PERFETTO_BUILDFLAG(PERFETTO_LOCAL_SYMBOLIZER)
-
 bool ParseLlvmSymbolizerLine(const std::string& line,
                              std::string* file_name,
                              uint32_t* line_no);
+std::vector<std::string> GetLines(
+    std::function<int64_t(char*, size_t)> fn_read);
 
 struct FoundBinary {
   std::string file_name;
@@ -82,22 +83,6 @@
   std::map<std::string, base::Optional<FoundBinary>> cache_;
 };
 
-class Subprocess {
- public:
-  Subprocess(const std::string& file, std::vector<std::string> args);
-
-  ~Subprocess();
-
-  int read_fd() { return output_pipe_.rd.get(); }
-  int write_fd() { return input_pipe_.wr.get(); }
-
- private:
-  base::Pipe input_pipe_;
-  base::Pipe output_pipe_;
-
-  pid_t pid_ = -1;
-};
-
 class LLVMSymbolizerProcess {
  public:
   LLVMSymbolizerProcess();
@@ -107,7 +92,6 @@
 
  private:
   Subprocess subprocess_;
-  FILE* read_file_;
 };
 
 class LocalSymbolizer : public Symbolizer {
@@ -128,8 +112,6 @@
   std::unique_ptr<BinaryFinder> finder_;
 };
 
-#endif  // PERFETTO_BUILDFLAG(PERFETTO_LOCAL_SYMBOLIZER)
-
 std::unique_ptr<Symbolizer> LocalSymbolizerOrDie(
     std::vector<std::string> binary_path,
     const char* mode);
diff --git a/src/profiling/symbolizer/local_symbolizer_unittest.cc b/src/profiling/symbolizer/local_symbolizer_unittest.cc
index b515866..72841f0 100644
--- a/src/profiling/symbolizer/local_symbolizer_unittest.cc
+++ b/src/profiling/symbolizer/local_symbolizer_unittest.cc
@@ -20,12 +20,29 @@
 // This translation unit is built only on Linux and MacOS. See //gn/BUILD.gn.
 #if PERFETTO_BUILDFLAG(PERFETTO_LOCAL_SYMBOLIZER)
 
+#include "src/base/test/utils.h"
 #include "src/profiling/symbolizer/local_symbolizer.h"
+#include "src/profiling/symbolizer/subprocess.h"
 
 namespace perfetto {
 namespace profiling {
 namespace {
 
+void RunAndValidateParseLines(std::string raw_contents) {
+  std::istringstream stream(raw_contents);
+  auto read_callback = [&stream](char* buffer, size_t size) {
+    stream.get(buffer, static_cast<int>(size), '\0');
+    return strlen(buffer);
+  };
+  std::vector<std::string> lines = perfetto::profiling::GetLines(read_callback);
+  std::istringstream validation(raw_contents);
+  for (std::string actual : lines) {
+    std::string expected;
+    getline(validation, expected);
+    EXPECT_EQ(actual, expected);
+  }
+}
+
 TEST(LocalSymbolizerTest, ParseLineWindows) {
   std::string file_name;
   uint32_t lineno;
@@ -35,6 +52,26 @@
   EXPECT_EQ(lineno, 123u);
 }
 
+TEST(LocalSymbolizerTest, ParseLinesExpectedOutput) {
+  std::string raw_contents =
+      "FSlateRHIRenderingPolicy::DrawElements(FRHICommandListImmediate&, "
+      "FSlateBackBuffer&, TRefCountPtr<FRHITexture2D>&, "
+      "TRefCountPtr<FRHITexture2D>&, TRefCountPtr<FRHITexture2D>&, int, "
+      "TArray<FSlateRenderBatch, TSizedDefaultAllocator<32> > const&, "
+      "FSlateRenderingParams const&)\n"
+      "F:/P4/EngineReleaseA/Engine/Source/Runtime/SlateRHIRenderer/"
+      "Private\\SlateRHIRenderingPolicy.cpp:1187:19\n";
+  RunAndValidateParseLines(raw_contents);
+}
+
+TEST(LocalSymbolizerTest, ParseLinesErrorOutput) {
+  std::string raw_contents =
+      "LLVMSymbolizer: error reading file: No such file or directory\n"
+      "??\n"
+      "??:0:0\n";
+  RunAndValidateParseLines(raw_contents);
+}
+
 }  // namespace
 }  // namespace profiling
 }  // namespace perfetto
diff --git a/src/profiling/symbolizer/scoped_read_mmap.h b/src/profiling/symbolizer/scoped_read_mmap.h
new file mode 100644
index 0000000..69a028a
--- /dev/null
+++ b/src/profiling/symbolizer/scoped_read_mmap.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2020 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_PROFILING_SYMBOLIZER_SCOPED_READ_MMAP_H_
+#define SRC_PROFILING_SYMBOLIZER_SCOPED_READ_MMAP_H_
+
+#include "perfetto/ext/base/scoped_file.h"
+
+namespace perfetto {
+namespace profiling {
+
+class ScopedReadMmap {
+ public:
+  ScopedReadMmap(const char* fname, size_t length);
+  virtual ~ScopedReadMmap();
+
+  void* operator*() { return ptr_; }
+
+  bool IsValid();
+
+ private:
+  size_t length_;
+  void* ptr_;
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+  void* file_ = nullptr;
+  void* map_ = nullptr;
+#else
+  base::ScopedFile fd_;
+#endif
+};
+
+}  // namespace profiling
+}  // namespace perfetto
+
+#endif  // SRC_PROFILING_SYMBOLIZER_SCOPED_READ_MMAP_H_
diff --git a/src/profiling/symbolizer/scoped_read_mmap_posix.cc b/src/profiling/symbolizer/scoped_read_mmap_posix.cc
new file mode 100644
index 0000000..93944fc
--- /dev/null
+++ b/src/profiling/symbolizer/scoped_read_mmap_posix.cc
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2020 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/profiling/symbolizer/scoped_read_mmap.h"
+
+#include "perfetto/base/logging.h"
+
+#include <sys/mman.h>
+
+namespace perfetto {
+namespace profiling {
+
+ScopedReadMmap::ScopedReadMmap(const char* fname, size_t length)
+    : length_(length), fd_(base::OpenFile(fname, O_RDONLY)) {
+  if (!fd_) {
+    PERFETTO_PLOG("Failed to open %s", fname);
+    return;
+  }
+  ptr_ = mmap(nullptr, length, PROT_READ, MAP_PRIVATE, *fd_, 0);
+}
+
+ScopedReadMmap::~ScopedReadMmap() {
+  if (ptr_ != MAP_FAILED)
+    munmap(ptr_, length_);
+}
+
+bool ScopedReadMmap::IsValid() {
+  return ptr_ != MAP_FAILED;
+}
+
+}  // namespace profiling
+}  // namespace perfetto
diff --git a/src/profiling/symbolizer/scoped_read_mmap_windows.cc b/src/profiling/symbolizer/scoped_read_mmap_windows.cc
new file mode 100644
index 0000000..6fc9fdd
--- /dev/null
+++ b/src/profiling/symbolizer/scoped_read_mmap_windows.cc
@@ -0,0 +1,62 @@
+
+/*
+ * Copyright (C) 2020 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/profiling/symbolizer/scoped_read_mmap.h"
+
+#define WIN32_MEAN_AND_LEAN
+#include <Windows.h>
+
+namespace perfetto {
+namespace profiling {
+
+ScopedReadMmap::ScopedReadMmap(const char* fName, size_t length)
+    : length_(length), ptr_(nullptr) {
+  file_ = CreateFileA(fName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING,
+                      FILE_ATTRIBUTE_NORMAL, NULL);
+  if (file_ == INVALID_HANDLE_VALUE) {
+    PERFETTO_DLOG("Failed to open file: %s", fName);
+    return;
+  }
+  map_ = CreateFileMapping(file_, NULL, PAGE_READONLY, 0, 0, NULL);
+  if (map_ == INVALID_HANDLE_VALUE) {
+    PERFETTO_DLOG("Failed to mmap file");
+    return;
+  }
+  ptr_ = MapViewOfFile(map_, FILE_MAP_READ, 0, 0, length);
+  if (ptr_ == nullptr) {
+    PERFETTO_DLOG("Failed to map view of file");
+  }
+}
+
+ScopedReadMmap::~ScopedReadMmap() {
+  if (ptr_ != nullptr) {
+    UnmapViewOfFile(ptr_);
+  }
+  if (map_ != nullptr && map_ != INVALID_HANDLE_VALUE) {
+    CloseHandle(map_);
+  }
+  if (file_ != nullptr && file_ != INVALID_HANDLE_VALUE) {
+    CloseHandle(file_);
+  }
+}
+
+bool ScopedReadMmap::IsValid() {
+  return ptr_ != nullptr;
+}
+
+}  // namespace profiling
+}  // namespace perfetto
\ No newline at end of file
diff --git a/src/profiling/symbolizer/subprocess.h b/src/profiling/symbolizer/subprocess.h
new file mode 100644
index 0000000..227d4d4
--- /dev/null
+++ b/src/profiling/symbolizer/subprocess.h
@@ -0,0 +1,58 @@
+
+/*
+ * Copyright (C) 2020 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_PROFILING_SYMBOLIZER_SUBPROCESS_H_
+#define SRC_PROFILING_SYMBOLIZER_SUBPROCESS_H_
+
+#include <string>
+#include <vector>
+
+#include "perfetto/base/build_config.h"
+
+#if !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+#include "perfetto/ext/base/pipe.h"
+#endif
+
+namespace perfetto {
+namespace profiling {
+
+class Subprocess {
+ public:
+  Subprocess(const std::string& file, std::vector<std::string> args);
+  ~Subprocess();
+
+  int64_t Write(const char* buffer, size_t size);
+  int64_t Read(char* buffer, size_t size);
+
+ private:
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+  void* child_pipe_in_read_ = nullptr;
+  void* child_pipe_in_write_ = nullptr;
+  void* child_pipe_out_read_ = nullptr;
+  void* child_pipe_out_write_ = nullptr;
+#else
+  base::Pipe input_pipe_;
+  base::Pipe output_pipe_;
+
+  pid_t pid_ = -1;
+#endif
+};
+
+}  // namespace profiling
+}  // namespace perfetto
+
+#endif  // SRC_PROFILING_SYMBOLIZER_SUBPROCESS_H_
diff --git a/src/profiling/symbolizer/subprocess_posix.cc b/src/profiling/symbolizer/subprocess_posix.cc
new file mode 100644
index 0000000..de7527a
--- /dev/null
+++ b/src/profiling/symbolizer/subprocess_posix.cc
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2020 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/profiling/symbolizer/subprocess.h"
+
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "perfetto/ext/base/utils.h"
+
+namespace perfetto {
+namespace profiling {
+
+Subprocess::Subprocess(const std::string& file, std::vector<std::string> args)
+    : input_pipe_(base::Pipe::Create(base::Pipe::kBothBlock)),
+      output_pipe_(base::Pipe::Create(base::Pipe::kBothBlock)) {
+  std::vector<char*> c_str_args;
+  for (std::string& arg : args)
+    c_str_args.push_back(&(arg[0]));
+  c_str_args.push_back(nullptr);
+
+  if ((pid_ = fork()) == 0) {
+    // Child
+    PERFETTO_CHECK(dup2(*input_pipe_.rd, STDIN_FILENO) != -1);
+    PERFETTO_CHECK(dup2(*output_pipe_.wr, STDOUT_FILENO) != -1);
+    input_pipe_.wr.reset();
+    output_pipe_.rd.reset();
+    if (execvp(file.c_str(), c_str_args.data()) == -1)
+      PERFETTO_FATAL("Failed to exec %s", file.c_str());
+  }
+  PERFETTO_CHECK(pid_ != -1);
+  input_pipe_.rd.reset();
+  output_pipe_.wr.reset();
+}
+
+Subprocess::~Subprocess() {
+  if (pid_ != -1) {
+    kill(pid_, SIGKILL);
+    int wstatus;
+    PERFETTO_EINTR(waitpid(pid_, &wstatus, 0));
+  }
+}
+
+int64_t Subprocess::Write(const char* buffer, size_t size) {
+  if (!input_pipe_.wr) {
+    return -1;
+  }
+  return PERFETTO_EINTR(write(input_pipe_.wr.get(), buffer, size));
+}
+
+int64_t Subprocess::Read(char* buffer, size_t size) {
+  if (!output_pipe_.rd) {
+    return -1;
+  }
+  return PERFETTO_EINTR(read(output_pipe_.rd.get(), buffer, size));
+}
+
+}  // namespace profiling
+}  // namespace perfetto
diff --git a/src/profiling/symbolizer/subprocess_windows.cc b/src/profiling/symbolizer/subprocess_windows.cc
new file mode 100644
index 0000000..01dbe95
--- /dev/null
+++ b/src/profiling/symbolizer/subprocess_windows.cc
@@ -0,0 +1,127 @@
+
+/*
+ * Copyright (C) 2020 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/profiling/symbolizer/subprocess.h"
+
+#include <sstream>
+#include <string>
+#define WIN32_MEAN_AND_LEAN
+#include <Windows.h>
+
+#include "perfetto/base/logging.h"
+
+namespace perfetto {
+namespace profiling {
+
+Subprocess::Subprocess(const std::string& file, std::vector<std::string> args) {
+  std::stringstream cmd;
+  cmd << file;
+  for (auto arg : args) {
+    cmd << " " << arg;
+  }
+  SECURITY_ATTRIBUTES attr;
+  attr.nLength = sizeof(SECURITY_ATTRIBUTES);
+  attr.bInheritHandle = true;
+  attr.lpSecurityDescriptor = NULL;
+  // Create a pipe for the child process's STDOUT.
+  if (!CreatePipe(&child_pipe_out_read_, &child_pipe_out_write_, &attr, 0) ||
+      !SetHandleInformation(child_pipe_out_read_, HANDLE_FLAG_INHERIT, 0)) {
+    PERFETTO_ELOG("Failed to create stdout pipe");
+    return;
+  }
+  if (!CreatePipe(&child_pipe_in_read_, &child_pipe_in_write_, &attr, 0) ||
+      !SetHandleInformation(child_pipe_in_write_, HANDLE_FLAG_INHERIT, 0)) {
+    PERFETTO_ELOG("Failed to create stdin pipe");
+    return;
+  }
+
+  PROCESS_INFORMATION proc_info;
+  STARTUPINFOA start_info;
+  bool success = false;
+  // Set up members of the PROCESS_INFORMATION structure.
+  ZeroMemory(&proc_info, sizeof(PROCESS_INFORMATION));
+
+  // Set up members of the STARTUPINFO structure.
+  // This structure specifies the STDIN and STDOUT handles for redirection.
+  ZeroMemory(&start_info, sizeof(STARTUPINFOA));
+  start_info.cb = sizeof(STARTUPINFOA);
+  start_info.hStdError = child_pipe_out_write_;
+  start_info.hStdOutput = child_pipe_out_write_;
+  start_info.hStdInput = child_pipe_in_read_;
+  start_info.dwFlags |= STARTF_USESTDHANDLES;
+
+  // Create the child process.
+  success = CreateProcessA(NULL,
+                           &(cmd.str()[0]),  // command line
+                           NULL,             // process security attributes
+                           NULL,         // primary thread security attributes
+                           TRUE,         // handles are inherited
+                           0,            // creation flags
+                           NULL,         // use parent's environment
+                           NULL,         // use parent's current directory
+                           &start_info,  // STARTUPINFO pointer
+                           &proc_info);  // receives PROCESS_INFORMATION
+
+  // If an error occurs, exit the application.
+  if (success) {
+    CloseHandle(proc_info.hProcess);
+    CloseHandle(proc_info.hThread);
+
+    // Close handles to the stdin and stdout pipes no longer needed by the child
+    // process. If they are not explicitly closed, there is no way to recognize
+    // that the child process has ended.
+
+    CloseHandle(child_pipe_out_write_);
+    CloseHandle(child_pipe_in_read_);
+  } else {
+    PERFETTO_ELOG("Failed to launch: %s", cmd.str().c_str());
+    child_pipe_in_read_ = nullptr;
+    child_pipe_in_write_ = nullptr;
+    child_pipe_out_write_ = nullptr;
+    child_pipe_out_read_ = nullptr;
+  }
+}
+
+Subprocess::~Subprocess() {
+  CloseHandle(child_pipe_out_read_);
+  CloseHandle(child_pipe_in_write_);
+}
+
+size_t Subprocess::Write(const char* buffer, size_t size) {
+  if (child_pipe_in_write_ == nullptr) {
+    return -1;
+  }
+  DWORD bytes_written;
+  if (WriteFile(child_pipe_in_write_, buffer, size, &bytes_written, NULL)) {
+    return static_cast<int64_t>(bytes_written);
+  }
+  return -1;
+}
+
+int64_t Subprocess::Read(char* buffer, size_t size) {
+  if (child_pipe_out_read_ == nullptr) {
+    return -1;
+  }
+  DWORD bytes_read;
+  if (ReadFile(child_pipe_out_read_, buffer, size, &bytes_read, NULL)) {
+    return static_cast<int64_t>(bytes_read);
+  }
+  return -1;
+}
+
+}  // namespace profiling
+}  // namespace perfetto