Bug 1466783 - Avoid copying while passing the profiler data with IPC r?mstange draft
authorNazım Can Altınova <canaltinova@gmail.com>
Fri, 15 Jun 2018 11:16:41 -0700
changeset 814439 bc56e831655e3536dfa04c211b63fa7d60a63a93
parent 810007 15c95df467be553beb39f2e8102c206639e05fde
push id115212
push userbmo:canaltinova@gmail.com
push dateThu, 05 Jul 2018 11:51:06 +0000
reviewersmstange
bugs1466783
milestone62.0a1
Bug 1466783 - Avoid copying while passing the profiler data with IPC r?mstange MozReview-Commit-ID: HfskcLojToC
tools/profiler/core/ProfileJSONWriter.cpp
tools/profiler/core/ProfileJSONWriter.h
tools/profiler/core/platform.cpp
tools/profiler/gecko/ProfilerChild.cpp
tools/profiler/public/ProfilerChild.h
--- a/tools/profiler/core/ProfileJSONWriter.cpp
+++ b/tools/profiler/core/ProfileJSONWriter.cpp
@@ -32,33 +32,50 @@ ChunkedJSONWriteFunc::Write(const char* 
   }
 
   memcpy(mChunkPtr, aStr, len);
   *newPtr = '\0';
   mChunkPtr = newPtr;
   mChunkLengths.back() += len;
 }
 
-mozilla::UniquePtr<char[]>
-ChunkedJSONWriteFunc::CopyData() const
+size_t
+ChunkedJSONWriteFunc::GetTotalLength() const
 {
   MOZ_ASSERT(mChunkLengths.length() == mChunkList.length());
   size_t totalLen = 1;
   for (size_t i = 0; i < mChunkLengths.length(); i++) {
     MOZ_ASSERT(strlen(mChunkList[i].get()) == mChunkLengths[i]);
     totalLen += mChunkLengths[i];
   }
-  mozilla::UniquePtr<char[]> c = mozilla::MakeUnique<char[]>(totalLen);
-  char* ptr = c.get();
+  return totalLen;
+}
+
+void
+ChunkedJSONWriteFunc::CopyDataIntoLazilyAllocatedBuffer(
+  const std::function<char*(size_t)>& aAllocator) const
+{
+  size_t totalLen = GetTotalLength();
+  char* ptr = aAllocator(totalLen);
   for (size_t i = 0; i < mChunkList.length(); i++) {
     size_t len = mChunkLengths[i];
     memcpy(ptr, mChunkList[i].get(), len);
     ptr += len;
   }
   *ptr = '\0';
+}
+
+mozilla::UniquePtr<char[]>
+ChunkedJSONWriteFunc::CopyData() const
+{
+  mozilla::UniquePtr<char[]> c;
+  CopyDataIntoLazilyAllocatedBuffer([&](size_t allocationSize) {
+    c = mozilla::MakeUnique<char[]>(allocationSize);
+    return c.get();
+  });
   return c;
 }
 
 void
 ChunkedJSONWriteFunc::Take(ChunkedJSONWriteFunc&& aOther)
 {
   for (size_t i = 0; i < aOther.mChunkList.length(); i++) {
     MOZ_ALWAYS_TRUE(mChunkLengths.append(aOther.mChunkLengths[i]));
--- a/tools/profiler/core/ProfileJSONWriter.h
+++ b/tools/profiler/core/ProfileJSONWriter.h
@@ -32,18 +32,23 @@ public:
   bool IsEmpty() const {
     MOZ_ASSERT_IF(!mChunkPtr, !mChunkEnd &&
                               mChunkList.length() == 0 &&
                               mChunkLengths.length() == 0);
     return !mChunkPtr;
   }
 
   void Write(const char* aStr) override;
+  void CopyDataIntoLazilyAllocatedBuffer(
+    const std::function<char*(size_t)>& aAllocator) const;
   mozilla::UniquePtr<char[]> CopyData() const;
   void Take(ChunkedJSONWriteFunc&& aOther);
+  // Returns the byte length of the complete combined string, including the
+  // null terminator byte.
+  size_t GetTotalLength() const;
 
 private:
   void AllocChunk(size_t aChunkSize);
 
   static const size_t kChunkSize = 4096 * 512;
 
   // Pointer for writing inside the current chunk.
   //
--- a/tools/profiler/core/platform.cpp
+++ b/tools/profiler/core/platform.cpp
@@ -2652,39 +2652,67 @@ profiler_shutdown()
   // profiler_stop() explain why.
   if (samplerThread) {
     ProfilerParent::ProfilerStopped();
     NotifyObservers("profiler-stopped");
     delete samplerThread;
   }
 }
 
+static bool
+WriteProfileToJSONWriter(SpliceableChunkedJSONWriter& aWriter,
+                         double aSinceTime,
+                         bool aIsShuttingDown)
+{
+  LOG("WriteProfileToJSONWriter");
+
+  MOZ_RELEASE_ASSERT(CorePS::Exists());
+
+  aWriter.Start();
+  {
+    if (!profiler_stream_json_for_this_process(
+          aWriter, aSinceTime, aIsShuttingDown)) {
+      return false;
+    }
+
+    // Don't include profiles from other processes because this is a
+    // synchronous function.
+    aWriter.StartArrayProperty("processes");
+    aWriter.EndArray();
+  }
+  aWriter.End();
+  return true;
+}
+
 UniquePtr<char[]>
 profiler_get_profile(double aSinceTime, bool aIsShuttingDown)
 {
   LOG("profiler_get_profile");
 
-  MOZ_RELEASE_ASSERT(CorePS::Exists());
+  SpliceableChunkedJSONWriter b;
+  if (!WriteProfileToJSONWriter(b, aSinceTime, aIsShuttingDown)) {
+    return nullptr;
+  }
+  return b.WriteFunc()->CopyData();
+}
+
+void
+profiler_get_profile_json_into_lazily_allocated_buffer(
+  const std::function<char*(size_t)>& aAllocator,
+  double aSinceTime,
+  bool aIsShuttingDown)
+{
+  LOG("profiler_get_profile_json_into_lazily_allocated_buffer");
 
   SpliceableChunkedJSONWriter b;
-  b.Start();
-  {
-    if (!profiler_stream_json_for_this_process(b, aSinceTime,
-                                               aIsShuttingDown)) {
-      return nullptr;
-    }
-
-    // Don't include profiles from other processes because this is a
-    // synchronous function.
-    b.StartArrayProperty("processes");
-    b.EndArray();
+  if (!WriteProfileToJSONWriter(b, aSinceTime, aIsShuttingDown)) {
+    return;
   }
-  b.End();
-
-  return b.WriteFunc()->CopyData();
+
+  b.WriteFunc()->CopyDataIntoLazilyAllocatedBuffer(aAllocator);
 }
 
 void
 profiler_get_start_params(int* aEntries, double* aInterval, uint32_t* aFeatures,
                           Vector<const char*>* aFilters)
 {
   MOZ_RELEASE_ASSERT(CorePS::Exists());
 
--- a/tools/profiler/gecko/ProfilerChild.cpp
+++ b/tools/profiler/gecko/ProfilerChild.cpp
@@ -83,36 +83,32 @@ CollectProfileOrEmptyString(bool aIsShut
   if (profile) {
     profileCString = nsCString(profile.get(), strlen(profile.get()));
   } else {
     profileCString = EmptyCString();
   }
   return profileCString;
 }
 
-Shmem
-ProfilerChild::ConvertProfileStringToShmem(const nsCString& aProfileCString) {
-  Shmem shmem;
-  if (!AllocShmem(aProfileCString.Length(),
-                  SharedMemory::TYPE_BASIC,
-                  &shmem)) {
-    return shmem;
-  }
-
-  PodCopy(shmem.get<char>(),
-          aProfileCString.BeginReading(),
-          aProfileCString.Length());
-  return shmem;
-}
-
 mozilla::ipc::IPCResult
 ProfilerChild::RecvGatherProfile(GatherProfileResolver&& aResolve)
 {
-  nsCString profile = CollectProfileOrEmptyString(/* aIsShuttingDown */ false);
-  aResolve(ConvertProfileStringToShmem(profile));
+  mozilla::ipc::Shmem shmem;
+  profiler_get_profile_json_into_lazily_allocated_buffer(
+    [&](size_t allocationSize) -> char* {
+      if (AllocShmem(allocationSize,
+                     mozilla::ipc::Shmem::SharedMemory::TYPE_BASIC,
+                     &shmem)) {
+        return shmem.get<char>();
+      }
+      return nullptr;
+    },
+    /* aSinceTime */ 0,
+    /* aIsShuttingDown */ false);
+  aResolve(std::move(shmem));
   return IPC_OK();
 }
 
 void
 ProfilerChild::ActorDestroy(ActorDestroyReason aActorDestroyReason)
 {
   mDestroyed = true;
 }
--- a/tools/profiler/public/ProfilerChild.h
+++ b/tools/profiler/public/ProfilerChild.h
@@ -39,17 +39,16 @@ private:
   mozilla::ipc::IPCResult RecvStart(const ProfilerInitParams& params) override;
   mozilla::ipc::IPCResult RecvEnsureStarted(const ProfilerInitParams& params) override;
   mozilla::ipc::IPCResult RecvStop() override;
   mozilla::ipc::IPCResult RecvPause() override;
   mozilla::ipc::IPCResult RecvResume() override;
   mozilla::ipc::IPCResult RecvGatherProfile(GatherProfileResolver&& aResolve) override;
 
   void ActorDestroy(ActorDestroyReason aActorDestroyReason) override;
-  Shmem ConvertProfileStringToShmem(const nsCString& profile);
 
   FORWARD_SHMEM_ALLOCATOR_TO(PProfilerChild)
 
   nsCOMPtr<nsIThread> mThread;
   bool mDestroyed;
 };
 
 } // namespace mozilla