LCOV - code coverage report
Current view: top level - tools/memory-profiler - MemoryProfiler.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 2 183 1.1 %
Date: 2017-07-14 16:53:18 Functions: 0 14 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2             : /* vim: set ts=2 et sw=2 tw=80: */
       3             : /* This Source Code Form is subject to the terms of the Mozilla Public
       4             :  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
       5             :  * You can obtain one at http://mozilla.org/MPL/2.0/. */
       6             : 
       7             : #include "MemoryProfiler.h"
       8             : 
       9             : #include <cmath>
      10             : #include <cstdlib>
      11             : 
      12             : #include "mozilla/ClearOnShutdown.h"
      13             : #include "mozilla/Move.h"
      14             : #include "mozilla/TimeStamp.h"
      15             : #include "mozilla/UniquePtr.h"
      16             : 
      17             : #include "GCHeapProfilerImpl.h"
      18             : #include "GeckoProfiler.h"
      19             : #include "NativeProfilerImpl.h"
      20             : #include "UncensoredAllocator.h"
      21             : #include "js/TypeDecls.h"
      22             : #include "jsfriendapi.h"
      23             : #include "nsIDOMClassInfo.h"
      24             : #include "nsIGlobalObject.h"
      25             : #include "prtime.h"
      26             : #include "xpcprivate.h"
      27             : 
      28             : namespace mozilla {
      29             : 
      30             : #define MEMORY_PROFILER_SAMPLE_SIZE 65536
      31             : #define BACKTRACE_BUFFER_SIZE 16384
      32             : 
      33           0 : ProfilerImpl::ProfilerImpl()
      34           0 :   : mSampleSize(MEMORY_PROFILER_SAMPLE_SIZE)
      35             : {
      36           0 :   mLog1minusP = std::log(1.0 - 1.0 / mSampleSize);
      37           0 :   mRemainingBytes = std::floor(std::log(1.0 - DRandom()) / mLog1minusP);
      38           0 : }
      39             : 
      40             : nsTArray<nsCString>
      41           0 : ProfilerImpl::GetStacktrace()
      42             : {
      43           0 :   nsTArray<nsCString> trace;
      44           0 :   auto output = MakeUnique<char[]>(BACKTRACE_BUFFER_SIZE);
      45             : 
      46           0 :   profiler_get_backtrace_noalloc(output.get(), BACKTRACE_BUFFER_SIZE);
      47           0 :   for (const char* p = output.get(); *p; p += strlen(p) + 1) {
      48           0 :     trace.AppendElement()->Assign(p);
      49             :   }
      50             : 
      51           0 :   return trace;
      52             : }
      53             : 
      54             : // Generate a random number in [0, 1).
      55             : double
      56           0 : ProfilerImpl::DRandom()
      57             : {
      58           0 :   return double(((uint64_t(std::rand()) & ((1 << 26) - 1)) << 27) +
      59           0 :                 (uint64_t(std::rand()) & ((1 << 27) - 1)))
      60           0 :     / (uint64_t(1) << 53);
      61             : }
      62             : 
      63             : size_t
      64           0 : ProfilerImpl::AddBytesSampled(uint32_t aBytes)
      65             : {
      66           0 :   size_t nSamples = 0;
      67           0 :   while (mRemainingBytes <= aBytes) {
      68           0 :     mRemainingBytes += std::floor(std::log(1.0 - DRandom()) / mLog1minusP);
      69           0 :     nSamples++;
      70             :   }
      71           0 :   mRemainingBytes -= aBytes;
      72           0 :   return nSamples;
      73             : }
      74             : 
      75           0 : NS_IMPL_ISUPPORTS(MemoryProfiler, nsIMemoryProfiler)
      76             : 
      77             : PRLock* MemoryProfiler::sLock;
      78             : uint32_t MemoryProfiler::sProfileContextCount;
      79           3 : StaticAutoPtr<NativeProfilerImpl> MemoryProfiler::sNativeProfiler;
      80           3 : StaticAutoPtr<JSContextProfilerMap> MemoryProfiler::sJSContextProfilerMap;
      81             : TimeStamp MemoryProfiler::sStartTime;
      82             : 
      83             : void
      84           0 : MemoryProfiler::InitOnce()
      85             : {
      86           0 :   MOZ_ASSERT(NS_IsMainThread());
      87             : 
      88             :   static bool initialized = false;
      89             : 
      90           0 :   if (!initialized) {
      91           0 :     MallocHook::Initialize();
      92           0 :     sLock = PR_NewLock();
      93           0 :     sProfileContextCount = 0;
      94           0 :     sJSContextProfilerMap = new JSContextProfilerMap();
      95           0 :     ClearOnShutdown(&sJSContextProfilerMap);
      96           0 :     ClearOnShutdown(&sNativeProfiler);
      97           0 :     std::srand(PR_Now());
      98           0 :     sStartTime = TimeStamp::ProcessCreation();
      99           0 :     initialized = true;
     100             :   }
     101           0 : }
     102             : 
     103             : NS_IMETHODIMP
     104           0 : MemoryProfiler::StartProfiler()
     105             : {
     106           0 :   InitOnce();
     107           0 :   AutoUseUncensoredAllocator ua;
     108           0 :   AutoMPLock lock(sLock);
     109           0 :   JSContext* context = XPCJSContext::Get()->Context();
     110           0 :   ProfilerForJSContext profiler;
     111           0 :   if (!sJSContextProfilerMap->Get(context, &profiler) ||
     112           0 :       !profiler.mEnabled) {
     113           0 :     if (sProfileContextCount == 0) {
     114           0 :       js::EnableContextProfilingStack(context, true);
     115           0 :       if (!sNativeProfiler) {
     116           0 :         sNativeProfiler = new NativeProfilerImpl();
     117             :       }
     118           0 :       MemProfiler::SetNativeProfiler(sNativeProfiler);
     119             :     }
     120           0 :     GCHeapProfilerImpl* gp = new GCHeapProfilerImpl();
     121           0 :     profiler.mEnabled = true;
     122           0 :     profiler.mProfiler = gp;
     123           0 :     sJSContextProfilerMap->Put(context, profiler);
     124           0 :     MemProfiler::GetMemProfiler(context)->start(gp);
     125           0 :     if (sProfileContextCount == 0) {
     126           0 :       MallocHook::Enable(sNativeProfiler);
     127             :     }
     128           0 :     sProfileContextCount++;
     129             :   }
     130           0 :   return NS_OK;
     131             : }
     132             : 
     133             : NS_IMETHODIMP
     134           0 : MemoryProfiler::StopProfiler()
     135             : {
     136           0 :   InitOnce();
     137           0 :   AutoUseUncensoredAllocator ua;
     138           0 :   AutoMPLock lock(sLock);
     139           0 :   JSContext* context = XPCJSContext::Get()->Context();
     140           0 :   ProfilerForJSContext profiler;
     141           0 :   if (sJSContextProfilerMap->Get(context, &profiler) &&
     142           0 :       profiler.mEnabled) {
     143           0 :     MemProfiler::GetMemProfiler(context)->stop();
     144           0 :     if (--sProfileContextCount == 0) {
     145           0 :       MallocHook::Disable();
     146           0 :       MemProfiler::SetNativeProfiler(nullptr);
     147           0 :       js::EnableContextProfilingStack(context, false);
     148             :     }
     149           0 :     profiler.mEnabled = false;
     150           0 :     sJSContextProfilerMap->Put(context, profiler);
     151             :   }
     152           0 :   return NS_OK;
     153             : }
     154             : 
     155             : NS_IMETHODIMP
     156           0 : MemoryProfiler::ResetProfiler()
     157             : {
     158           0 :   InitOnce();
     159           0 :   AutoUseUncensoredAllocator ua;
     160           0 :   AutoMPLock lock(sLock);
     161           0 :   JSContext* context = XPCJSContext::Get()->Context();
     162           0 :   ProfilerForJSContext profiler;
     163           0 :   if (!sJSContextProfilerMap->Get(context, &profiler) ||
     164           0 :       !profiler.mEnabled) {
     165           0 :     delete profiler.mProfiler;
     166           0 :     profiler.mProfiler = nullptr;
     167           0 :     sJSContextProfilerMap->Put(context, profiler);
     168             :   }
     169           0 :   if (sProfileContextCount == 0) {
     170           0 :     sNativeProfiler = nullptr;
     171             :   }
     172           0 :   return NS_OK;
     173             : }
     174             : 
     175           0 : struct MergedTraces
     176             : {
     177             :   nsTArray<nsCString> mNames;
     178             :   nsTArray<TrieNode> mTraces;
     179             :   nsTArray<AllocEvent> mEvents;
     180             : };
     181             : 
     182             : // Merge events and corresponding traces and names.
     183             : static MergedTraces
     184           0 : MergeResults(const nsTArray<nsCString>& names0,
     185             :              const nsTArray<TrieNode>& traces0,
     186             :              const nsTArray<AllocEvent>& events0,
     187             :              const nsTArray<nsCString>& names1,
     188             :              const nsTArray<TrieNode>& traces1,
     189             :              const nsTArray<AllocEvent>& events1)
     190             : {
     191           0 :   NodeIndexMap<nsCStringHashKey, nsCString> names;
     192           0 :   NodeIndexMap<nsGenericHashKey<TrieNode>, TrieNode> traces;
     193           0 :   nsTArray<AllocEvent> events;
     194             : 
     195           0 :   nsTArray<size_t> names1Tonames0(names1.Length());
     196           0 :   nsTArray<size_t> traces1Totraces0(traces1.Length());
     197             : 
     198             :   // Merge names.
     199           0 :   for (auto& i: names0) {
     200           0 :     names.Insert(i);
     201             :   }
     202           0 :   for (auto& i: names1) {
     203           0 :     names1Tonames0.AppendElement(names.Insert(i));
     204             :   }
     205             : 
     206             :   // Merge traces. Note that traces1[i].parentIdx < i for all i > 0.
     207           0 :   for (auto& i: traces0) {
     208           0 :     traces.Insert(i);
     209             :   }
     210           0 :   traces1Totraces0.AppendElement(0);
     211           0 :   for (size_t i = 1; i < traces1.Length(); i++) {
     212           0 :     TrieNode node = traces1[i];
     213           0 :     node.parentIdx = traces1Totraces0[node.parentIdx];
     214           0 :     node.nameIdx = names1Tonames0[node.nameIdx];
     215           0 :     traces1Totraces0.AppendElement(traces.Insert(node));
     216             :   }
     217             : 
     218             :   // Merge the events according to timestamps.
     219           0 :   auto p0 = events0.begin();
     220           0 :   auto p1 = events1.begin();
     221             : 
     222           0 :   while (p0 != events0.end() && p1 != events1.end()) {
     223           0 :     if (p0->mTimestamp < p1->mTimestamp) {
     224           0 :       events.AppendElement(*p0++);
     225             :     } else {
     226           0 :       events.AppendElement(*p1++);
     227           0 :       events.LastElement().mTraceIdx =
     228           0 :         traces1Totraces0[events.LastElement().mTraceIdx];
     229             :     }
     230             :   }
     231             : 
     232           0 :   while (p0 != events0.end()) {
     233           0 :     events.AppendElement(*p0++);
     234             :   }
     235             : 
     236           0 :   while (p1 != events1.end()) {
     237           0 :     events.AppendElement(*p1++);
     238           0 :     events.LastElement().mTraceIdx =
     239           0 :       traces1Totraces0[events.LastElement().mTraceIdx];
     240             :   }
     241             : 
     242           0 :   return MergedTraces{names.Serialize(), traces.Serialize(), Move(events)};
     243             : }
     244             : 
     245             : NS_IMETHODIMP
     246           0 : MemoryProfiler::GetResults(JSContext* cx, JS::MutableHandle<JS::Value> aResult)
     247             : {
     248           0 :   InitOnce();
     249           0 :   AutoUseUncensoredAllocator ua;
     250           0 :   AutoMPLock lock(sLock);
     251           0 :   JSContext* context = XPCJSContext::Get()->Context();
     252             :   // Getting results when the profiler is running is not allowed.
     253           0 :   if (sProfileContextCount > 0) {
     254           0 :     return NS_OK;
     255             :   }
     256             :   // Return immediately when native profiler does not exist.
     257           0 :   if (!sNativeProfiler) {
     258           0 :     return NS_OK;
     259             :   }
     260             :   // Return immediately when there's no result in current context.
     261           0 :   ProfilerForJSContext profiler;
     262           0 :   if (!sJSContextProfilerMap->Get(context, &profiler) ||
     263           0 :       !profiler.mProfiler) {
     264           0 :     return NS_OK;
     265             :   }
     266           0 :   GCHeapProfilerImpl* gp = profiler.mProfiler;
     267             : 
     268           0 :   auto results = MergeResults(gp->GetNames(), gp->GetTraces(), gp->GetEvents(),
     269           0 :                               sNativeProfiler->GetNames(),
     270           0 :                               sNativeProfiler->GetTraces(),
     271           0 :                               sNativeProfiler->GetEvents());
     272           0 :   const nsTArray<nsCString>& names = results.mNames;
     273           0 :   const nsTArray<TrieNode>& traces = results.mTraces;
     274           0 :   const nsTArray<AllocEvent>& events = results.mEvents;
     275             : 
     276           0 :   JS::RootedObject jsnames(cx, JS_NewArrayObject(cx, names.Length()));
     277           0 :   JS::RootedObject jstraces(cx, JS_NewArrayObject(cx, traces.Length()));
     278           0 :   JS::RootedObject jsevents(cx, JS_NewArrayObject(cx, events.Length()));
     279             : 
     280           0 :   for (size_t i = 0; i < names.Length(); i++) {
     281           0 :     JS::RootedString name(cx, JS_NewStringCopyZ(cx, names[i].get()));
     282           0 :     JS_SetElement(cx, jsnames, i, name);
     283             :   }
     284             : 
     285           0 :   for (size_t i = 0; i < traces.Length(); i++) {
     286           0 :     JS::RootedObject tn(cx, JS_NewPlainObject(cx));
     287           0 :     JS::RootedValue nameIdx(cx, JS_NumberValue(traces[i].nameIdx));
     288           0 :     JS::RootedValue parentIdx(cx, JS_NumberValue(traces[i].parentIdx));
     289           0 :     JS_SetProperty(cx, tn, "nameIdx", nameIdx);
     290           0 :     JS_SetProperty(cx, tn, "parentIdx", parentIdx);
     291           0 :     JS_SetElement(cx, jstraces, i, tn);
     292             :   }
     293             : 
     294           0 :   int i = 0;
     295           0 :   for (auto ent: events) {
     296           0 :     if (ent.mSize == 0) {
     297           0 :       continue;
     298             :     }
     299           0 :     MOZ_ASSERT(!sStartTime.IsNull());
     300           0 :     double time = (ent.mTimestamp - sStartTime).ToMilliseconds();
     301           0 :     JS::RootedObject tn(cx, JS_NewPlainObject(cx));
     302           0 :     JS::RootedValue size(cx, JS_NumberValue(ent.mSize));
     303           0 :     JS::RootedValue traceIdx(cx, JS_NumberValue(ent.mTraceIdx));
     304           0 :     JS::RootedValue timestamp(cx, JS_NumberValue(time));
     305           0 :     JS_SetProperty(cx, tn, "size", size);
     306           0 :     JS_SetProperty(cx, tn, "traceIdx", traceIdx);
     307           0 :     JS_SetProperty(cx, tn, "timestamp", timestamp);
     308           0 :     JS_SetElement(cx, jsevents, i++, tn);
     309             :   }
     310           0 :   JS_SetArrayLength(cx, jsevents, i);
     311             : 
     312           0 :   JS::RootedObject result(cx, JS_NewPlainObject(cx));
     313           0 :   JS::RootedValue objnames(cx, ObjectOrNullValue(jsnames));
     314           0 :   JS_SetProperty(cx, result, "names", objnames);
     315           0 :   JS::RootedValue objtraces(cx, ObjectOrNullValue(jstraces));
     316           0 :   JS_SetProperty(cx, result, "traces", objtraces);
     317           0 :   JS::RootedValue objevents(cx, ObjectOrNullValue(jsevents));
     318           0 :   JS_SetProperty(cx, result, "events", objevents);
     319           0 :   aResult.setObject(*result);
     320           0 :   return NS_OK;
     321             : }
     322             : 
     323             : } // namespace mozilla

Generated by: LCOV version 1.13