1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3  * License, v. 2.0. If a copy of the MPL was not distributed with this
4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 
6 #include <algorithm>
7 #include <string>
8 #include <stdio.h>
9 #include <fstream>
10 #include <sstream>
11 #include "GeckoProfiler.h"
12 #ifndef SPS_STANDALONE
13 #include "SaveProfileTask.h"
14 #include "nsThreadUtils.h"
15 #include "prenv.h"
16 #include "prtime.h"
17 #include "nsXULAppAPI.h"
18 #endif
19 #include "ProfileEntry.h"
20 #include "SyncProfile.h"
21 #include "platform.h"
22 #include "shared-libraries.h"
23 #include "mozilla/StackWalk.h"
24 #include "GeckoSampler.h"
25 
26 // JSON
27 #include "ProfileJSONWriter.h"
28 
29 #ifndef SPS_STANDALONE
30 // Meta
31 #include "nsXPCOM.h"
32 #include "nsXPCOMCID.h"
33 #include "nsIHttpProtocolHandler.h"
34 #include "nsServiceManagerUtils.h"
35 #include "nsIXULRuntime.h"
36 #include "nsIXULAppInfo.h"
37 #include "nsDirectoryServiceUtils.h"
38 #include "nsDirectoryServiceDefs.h"
39 #include "nsIObserverService.h"
40 #include "mozilla/Services.h"
41 #include "PlatformMacros.h"
42 #include "nsTArray.h"
43 
44 #include "mozilla/ProfileGatherer.h"
45 #endif
46 
47 #if defined(SPS_OS_android) && !defined(MOZ_WIDGET_GONK)
48   #include "FennecJNIWrappers.h"
49 #endif
50 
51 #ifndef SPS_STANDALONE
52 // JS
53 #include "jsfriendapi.h"
54 #include "js/ProfilingFrameIterator.h"
55 #endif
56 
57 #if defined(MOZ_PROFILING) && (defined(XP_MACOSX) || defined(XP_WIN))
58  #define USE_NS_STACKWALK
59 #endif
60 
61 #if defined(XP_WIN)
62 typedef CONTEXT tickcontext_t;
63 #elif defined(LINUX)
64 #include <ucontext.h>
65 typedef ucontext_t tickcontext_t;
66 #endif
67 
68 #if defined(LINUX) || defined(XP_MACOSX)
69 #include <sys/types.h>
70 pid_t gettid();
71 #endif
72 
73 #if defined(__arm__) && defined(ANDROID)
74  // Should also work on ARM Linux, but not tested there yet.
75  #define USE_EHABI_STACKWALK
76 #endif
77 #ifdef USE_EHABI_STACKWALK
78  #include "EHABIStackWalk.h"
79 #endif
80 
81 #ifndef SPS_STANDALONE
82 #if defined(SPS_PLAT_amd64_linux) || defined(SPS_PLAT_x86_linux)
83 # define USE_LUL_STACKWALK
84 # include "lul/LulMain.h"
85 # include "lul/platform-linux-lul.h"
86 #endif
87 #endif
88 
89 using std::string;
90 using namespace mozilla;
91 
92 #ifndef MAXPATHLEN
93  #ifdef PATH_MAX
94   #define MAXPATHLEN PATH_MAX
95  #elif defined(MAX_PATH)
96   #define MAXPATHLEN MAX_PATH
97  #elif defined(_MAX_PATH)
98   #define MAXPATHLEN _MAX_PATH
99  #elif defined(CCHMAXPATH)
100   #define MAXPATHLEN CCHMAXPATH
101  #else
102   #define MAXPATHLEN 1024
103  #endif
104 #endif
105 
106 #ifdef MOZ_VALGRIND
107 # include <valgrind/memcheck.h>
108 #else
109 # define VALGRIND_MAKE_MEM_DEFINED(_addr,_len)   ((void)0)
110 #endif
111 
112 
113 ///////////////////////////////////////////////////////////////////////
114 // BEGIN SaveProfileTask et al
115 
116 static void
AddSharedLibraryInfoToStream(std::ostream & aStream,const SharedLibrary & aLib)117 AddSharedLibraryInfoToStream(std::ostream& aStream, const SharedLibrary& aLib)
118 {
119   aStream << "{";
120   aStream << "\"start\":" << aLib.GetStart();
121   aStream << ",\"end\":" << aLib.GetEnd();
122   aStream << ",\"offset\":" << aLib.GetOffset();
123   aStream << ",\"name\":\"" << aLib.GetName() << "\"";
124   const std::string &breakpadId = aLib.GetBreakpadId();
125   aStream << ",\"breakpadId\":\"" << breakpadId << "\"";
126 #ifdef XP_WIN
127   // FIXME: remove this XP_WIN code when the profiler plugin has switched to
128   // using breakpadId.
129   std::string pdbSignature = breakpadId.substr(0, 32);
130   std::string pdbAgeStr = breakpadId.substr(32,  breakpadId.size() - 1);
131 
132   std::stringstream stream;
133   stream << pdbAgeStr;
134 
135   unsigned pdbAge;
136   stream << std::hex;
137   stream >> pdbAge;
138 
139 #ifdef DEBUG
140   std::ostringstream oStream;
141   oStream << pdbSignature << std::hex << std::uppercase << pdbAge;
142   MOZ_ASSERT(breakpadId == oStream.str());
143 #endif
144 
145   aStream << ",\"pdbSignature\":\"" << pdbSignature << "\"";
146   aStream << ",\"pdbAge\":" << pdbAge;
147   aStream << ",\"pdbName\":\"" << aLib.GetName() << "\"";
148 #endif
149   aStream << "}";
150 }
151 
152 std::string
GetSharedLibraryInfoStringInternal()153 GetSharedLibraryInfoStringInternal()
154 {
155   SharedLibraryInfo info = SharedLibraryInfo::GetInfoForSelf();
156   if (info.GetSize() == 0)
157     return "[]";
158 
159   std::ostringstream os;
160   os << "[";
161   AddSharedLibraryInfoToStream(os, info.GetEntry(0));
162 
163   for (size_t i = 1; i < info.GetSize(); i++) {
164     os << ",";
165     AddSharedLibraryInfoToStream(os, info.GetEntry(i));
166   }
167 
168   os << "]";
169   return os.str();
170 }
171 
172 static bool
hasFeature(const char ** aFeatures,uint32_t aFeatureCount,const char * aFeature)173 hasFeature(const char** aFeatures, uint32_t aFeatureCount, const char* aFeature) {
174   for(size_t i = 0; i < aFeatureCount; i++) {
175     if (strcmp(aFeatures[i], aFeature) == 0)
176       return true;
177   }
178   return false;
179 }
180 
GeckoSampler(double aInterval,int aEntrySize,const char ** aFeatures,uint32_t aFeatureCount,const char ** aThreadNameFilters,uint32_t aFilterCount)181 GeckoSampler::GeckoSampler(double aInterval, int aEntrySize,
182                          const char** aFeatures, uint32_t aFeatureCount,
183                          const char** aThreadNameFilters, uint32_t aFilterCount)
184   : Sampler(aInterval, true, aEntrySize)
185   , mPrimaryThreadProfile(nullptr)
186   , mBuffer(new ProfileBuffer(aEntrySize))
187   , mSaveRequested(false)
188 #if defined(XP_WIN)
189   , mIntelPowerGadget(nullptr)
190 #endif
191 {
192   mUseStackWalk = hasFeature(aFeatures, aFeatureCount, "stackwalk");
193 
194   mProfileJS = hasFeature(aFeatures, aFeatureCount, "js");
195   mProfileGPU = hasFeature(aFeatures, aFeatureCount, "gpu");
196   mProfilePower = hasFeature(aFeatures, aFeatureCount, "power");
197   // Users sometimes ask to filter by a list of threads but forget to request
198   // profiling non main threads. Let's make it implificit if we have a filter
199   mProfileThreads = hasFeature(aFeatures, aFeatureCount, "threads") || aFilterCount > 0;
200   mAddLeafAddresses = hasFeature(aFeatures, aFeatureCount, "leaf");
201   mPrivacyMode = hasFeature(aFeatures, aFeatureCount, "privacy");
202   mAddMainThreadIO = hasFeature(aFeatures, aFeatureCount, "mainthreadio");
203   mProfileMemory = hasFeature(aFeatures, aFeatureCount, "memory");
204   mTaskTracer = hasFeature(aFeatures, aFeatureCount, "tasktracer");
205   mLayersDump = hasFeature(aFeatures, aFeatureCount, "layersdump");
206   mDisplayListDump = hasFeature(aFeatures, aFeatureCount, "displaylistdump");
207   mProfileRestyle = hasFeature(aFeatures, aFeatureCount, "restyle");
208 
209 #if defined(XP_WIN)
210   if (mProfilePower) {
211     mIntelPowerGadget = new IntelPowerGadget();
212     mProfilePower = mIntelPowerGadget->Init();
213   }
214 #endif
215 
216 #if defined(SPS_OS_android) && !defined(MOZ_WIDGET_GONK)
217   mProfileJava = mozilla::jni::IsFennec() &&
218       hasFeature(aFeatures, aFeatureCount, "java");
219 #else
220   mProfileJava = false;
221 #endif
222 
223   // Deep copy aThreadNameFilters
224   MOZ_ALWAYS_TRUE(mThreadNameFilters.resize(aFilterCount));
225   for (uint32_t i = 0; i < aFilterCount; ++i) {
226     mThreadNameFilters[i] = aThreadNameFilters[i];
227   }
228 
229   // Deep copy aFeatures
230   MOZ_ALWAYS_TRUE(mFeatures.resize(aFeatureCount));
231   for (uint32_t i = 0; i < aFeatureCount; ++i) {
232     mFeatures[i] = aFeatures[i];
233   }
234 
235   bool ignore;
236   sStartTime = mozilla::TimeStamp::ProcessCreation(ignore);
237 
238   {
239     ::MutexAutoLock lock(*sRegisteredThreadsMutex);
240 
241     // Create ThreadProfile for each registered thread
242     for (uint32_t i = 0; i < sRegisteredThreads->size(); i++) {
243       ThreadInfo* info = sRegisteredThreads->at(i);
244 
245       RegisterThread(info);
246     }
247 
248     SetActiveSampler(this);
249   }
250 
251 #ifdef MOZ_TASK_TRACER
252   if (mTaskTracer) {
253     mozilla::tasktracer::StartLogging();
254   }
255 #endif
256 
257   mGatherer = new mozilla::ProfileGatherer(this);
258 }
259 
~GeckoSampler()260 GeckoSampler::~GeckoSampler()
261 {
262   if (IsActive())
263     Stop();
264 
265   SetActiveSampler(nullptr);
266 
267   // Destroy ThreadProfile for all threads
268   {
269     ::MutexAutoLock lock(*sRegisteredThreadsMutex);
270 
271     for (uint32_t i = 0; i < sRegisteredThreads->size(); i++) {
272       ThreadInfo* info = sRegisteredThreads->at(i);
273       ThreadProfile* profile = info->Profile();
274       if (profile) {
275         delete profile;
276         info->SetProfile(nullptr);
277       }
278       // We've stopped profiling. We no longer need to retain
279       // information for an old thread.
280       if (info->IsPendingDelete()) {
281         delete info;
282         sRegisteredThreads->erase(sRegisteredThreads->begin() + i);
283         i--;
284       }
285     }
286   }
287 #if defined(XP_WIN)
288   delete mIntelPowerGadget;
289 #endif
290 
291   // Cancel any in-flight async profile gatherering
292   // requests
293   mGatherer->Cancel();
294 }
295 
HandleSaveRequest()296 void GeckoSampler::HandleSaveRequest()
297 {
298   if (!mSaveRequested)
299     return;
300   mSaveRequested = false;
301 
302 #ifndef SPS_STANDALONE
303   // TODO: Use use the ipc/chromium Tasks here to support processes
304   // without XPCOM.
305   nsCOMPtr<nsIRunnable> runnable = new SaveProfileTask();
306   NS_DispatchToMainThread(runnable);
307 #endif
308 }
309 
DeleteExpiredMarkers()310 void GeckoSampler::DeleteExpiredMarkers()
311 {
312   mBuffer->deleteExpiredStoredMarkers();
313 }
314 
StreamTaskTracer(SpliceableJSONWriter & aWriter)315 void GeckoSampler::StreamTaskTracer(SpliceableJSONWriter& aWriter)
316 {
317 #ifdef MOZ_TASK_TRACER
318   aWriter.StartArrayProperty("data");
319     UniquePtr<nsTArray<nsCString>> data = mozilla::tasktracer::GetLoggedData(sStartTime);
320     for (uint32_t i = 0; i < data->Length(); ++i) {
321       aWriter.StringElement((data->ElementAt(i)).get());
322     }
323   aWriter.EndArray();
324 
325   aWriter.StartArrayProperty("threads");
326     ::MutexAutoLock lock(*sRegisteredThreadsMutex);
327     for (size_t i = 0; i < sRegisteredThreads->size(); i++) {
328       // Thread meta data
329       ThreadInfo* info = sRegisteredThreads->at(i);
330       aWriter.StartObjectElement();
331         if (XRE_GetProcessType() == GeckoProcessType_Plugin) {
332           // TODO Add the proper plugin name
333           aWriter.StringProperty("name", "Plugin");
334         } else {
335           aWriter.StringProperty("name", info->Name());
336         }
337         aWriter.IntProperty("tid", static_cast<int>(info->ThreadId()));
338       aWriter.EndObject();
339     }
340   aWriter.EndArray();
341 
342   aWriter.DoubleProperty("start", static_cast<double>(mozilla::tasktracer::GetStartTime()));
343 #endif
344 }
345 
346 
StreamMetaJSCustomObject(SpliceableJSONWriter & aWriter)347 void GeckoSampler::StreamMetaJSCustomObject(SpliceableJSONWriter& aWriter)
348 {
349   aWriter.IntProperty("version", 3);
350   aWriter.DoubleProperty("interval", interval());
351   aWriter.IntProperty("stackwalk", mUseStackWalk);
352 
353 #ifndef SPS_STANDALONE
354   mozilla::TimeDuration delta = mozilla::TimeStamp::Now() - sStartTime;
355   aWriter.DoubleProperty("startTime", static_cast<double>(PR_Now()/1000.0 - delta.ToMilliseconds()));
356 
357   aWriter.IntProperty("processType", XRE_GetProcessType());
358 
359   nsresult res;
360   nsCOMPtr<nsIHttpProtocolHandler> http = do_GetService(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "http", &res);
361   if (!NS_FAILED(res)) {
362     nsAutoCString string;
363 
364     res = http->GetPlatform(string);
365     if (!NS_FAILED(res))
366       aWriter.StringProperty("platform", string.Data());
367 
368     res = http->GetOscpu(string);
369     if (!NS_FAILED(res))
370       aWriter.StringProperty("oscpu", string.Data());
371 
372     res = http->GetMisc(string);
373     if (!NS_FAILED(res))
374       aWriter.StringProperty("misc", string.Data());
375   }
376 
377   nsCOMPtr<nsIXULRuntime> runtime = do_GetService("@mozilla.org/xre/runtime;1");
378   if (runtime) {
379     nsAutoCString string;
380 
381     res = runtime->GetXPCOMABI(string);
382     if (!NS_FAILED(res))
383       aWriter.StringProperty("abi", string.Data());
384 
385     res = runtime->GetWidgetToolkit(string);
386     if (!NS_FAILED(res))
387       aWriter.StringProperty("toolkit", string.Data());
388   }
389 
390   nsCOMPtr<nsIXULAppInfo> appInfo = do_GetService("@mozilla.org/xre/app-info;1");
391   if (appInfo) {
392     nsAutoCString string;
393 
394     res = appInfo->GetName(string);
395     if (!NS_FAILED(res))
396       aWriter.StringProperty("product", string.Data());
397   }
398 #endif
399 }
400 
ToStreamAsJSON(std::ostream & stream,double aSinceTime)401 void GeckoSampler::ToStreamAsJSON(std::ostream& stream, double aSinceTime)
402 {
403   SpliceableJSONWriter b(mozilla::MakeUnique<OStreamJSONWriteFunc>(stream));
404   StreamJSON(b, aSinceTime);
405 }
406 
407 #ifndef SPS_STANDALONE
ToJSObject(JSContext * aCx,double aSinceTime)408 JSObject* GeckoSampler::ToJSObject(JSContext *aCx, double aSinceTime)
409 {
410   JS::RootedValue val(aCx);
411   {
412     UniquePtr<char[]> buf = ToJSON(aSinceTime);
413     NS_ConvertUTF8toUTF16 js_string(nsDependentCString(buf.get()));
414     MOZ_ALWAYS_TRUE(JS_ParseJSON(aCx, static_cast<const char16_t*>(js_string.get()),
415                                  js_string.Length(), &val));
416   }
417   return &val.toObject();
418 }
419 
GetGatherer(nsISupports ** aRetVal)420 void GeckoSampler::GetGatherer(nsISupports** aRetVal)
421 {
422   if (!aRetVal || NS_WARN_IF(!mGatherer)) {
423     return;
424   }
425   NS_ADDREF(*aRetVal = mGatherer);
426 }
427 #endif
428 
ToJSON(double aSinceTime)429 UniquePtr<char[]> GeckoSampler::ToJSON(double aSinceTime)
430 {
431   SpliceableChunkedJSONWriter b;
432   StreamJSON(b, aSinceTime);
433   return b.WriteFunc()->CopyData();
434 }
435 
ToJSObjectAsync(double aSinceTime,mozilla::dom::Promise * aPromise)436 void GeckoSampler::ToJSObjectAsync(double aSinceTime,
437                                   mozilla::dom::Promise* aPromise)
438 {
439   if (NS_WARN_IF(!mGatherer)) {
440     return;
441   }
442 
443   mGatherer->Start(aSinceTime, aPromise);
444 }
445 
446 struct SubprocessClosure {
SubprocessClosureSubprocessClosure447   explicit SubprocessClosure(SpliceableJSONWriter* aWriter)
448     : mWriter(aWriter)
449   {}
450 
451   SpliceableJSONWriter* mWriter;
452 };
453 
SubProcessCallback(const char * aProfile,void * aClosure)454 void SubProcessCallback(const char* aProfile, void* aClosure)
455 {
456   // Called by the observer to get their profile data included
457   // as a sub profile
458   SubprocessClosure* closure = (SubprocessClosure*)aClosure;
459 
460   // Add the string profile into the profile
461   closure->mWriter->StringElement(aProfile);
462 }
463 
464 
465 #if defined(SPS_OS_android) && !defined(MOZ_WIDGET_GONK)
466 static
BuildJavaThreadJSObject(SpliceableJSONWriter & aWriter)467 void BuildJavaThreadJSObject(SpliceableJSONWriter& aWriter)
468 {
469   aWriter.StringProperty("name", "Java Main Thread");
470 
471   aWriter.StartArrayProperty("samples");
472 
473     // for each sample
474     for (int sampleId = 0; true; sampleId++) {
475       bool firstRun = true;
476       // for each frame
477       for (int frameId = 0; true; frameId++) {
478         jni::String::LocalRef frameName =
479             java::GeckoJavaSampler::GetFrameName(0, sampleId, frameId);
480         // when we run out of frames, we stop looping
481         if (!frameName) {
482           // if we found at least one frame, we have objects to close
483           if (!firstRun) {
484               aWriter.EndArray();
485             aWriter.EndObject();
486           }
487           break;
488         }
489         // the first time around, open the sample object and frames array
490         if (firstRun) {
491           firstRun = false;
492 
493           double sampleTime =
494               java::GeckoJavaSampler::GetSampleTime(0, sampleId);
495 
496           aWriter.StartObjectElement();
497             aWriter.DoubleProperty("time", sampleTime);
498 
499             aWriter.StartArrayProperty("frames");
500         }
501         // add a frame to the sample
502         aWriter.StartObjectElement();
503           aWriter.StringProperty("location",
504                                  frameName->ToCString().BeginReading());
505         aWriter.EndObject();
506       }
507       // if we found no frames for this sample, we are done
508       if (firstRun) {
509         break;
510       }
511     }
512 
513   aWriter.EndArray();
514 }
515 #endif
516 
StreamJSON(SpliceableJSONWriter & aWriter,double aSinceTime)517 void GeckoSampler::StreamJSON(SpliceableJSONWriter& aWriter, double aSinceTime)
518 {
519   aWriter.Start(SpliceableJSONWriter::SingleLineStyle);
520   {
521     // Put shared library info
522     aWriter.StringProperty("libs", GetSharedLibraryInfoStringInternal().c_str());
523 
524     // Put meta data
525     aWriter.StartObjectProperty("meta");
526       StreamMetaJSCustomObject(aWriter);
527     aWriter.EndObject();
528 
529     // Data of TaskTracer doesn't belong in the circular buffer.
530     if (TaskTracer()) {
531       aWriter.StartObjectProperty("tasktracer");
532       StreamTaskTracer(aWriter);
533       aWriter.EndObject();
534     }
535 
536     // Lists the samples for each ThreadProfile
537     aWriter.StartArrayProperty("threads");
538     {
539       SetPaused(true);
540 
541       {
542         ::MutexAutoLock lock(*sRegisteredThreadsMutex);
543 
544         for (size_t i = 0; i < sRegisteredThreads->size(); i++) {
545           // Thread not being profiled, skip it
546           if (!sRegisteredThreads->at(i)->Profile())
547             continue;
548 
549           // Note that we intentionally include ThreadProfile which
550           // have been marked for pending delete.
551 
552           ::MutexAutoLock lock(sRegisteredThreads->at(i)->Profile()->GetMutex());
553 
554           sRegisteredThreads->at(i)->Profile()->StreamJSON(aWriter, aSinceTime);
555         }
556       }
557 
558 #ifndef SPS_STANDALONE
559       if (Sampler::CanNotifyObservers()) {
560         // Send a event asking any subprocesses (plugins) to
561         // give us their information
562         SubprocessClosure closure(&aWriter);
563         nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
564         if (os) {
565           RefPtr<ProfileSaveEvent> pse = new ProfileSaveEvent(SubProcessCallback, &closure);
566           os->NotifyObservers(pse, "profiler-subprocess", nullptr);
567         }
568       }
569 
570   #if defined(SPS_OS_android) && !defined(MOZ_WIDGET_GONK)
571       if (ProfileJava()) {
572         java::GeckoJavaSampler::Pause();
573 
574         aWriter.Start();
575         {
576           BuildJavaThreadJSObject(aWriter);
577         }
578         aWriter.End();
579 
580         java::GeckoJavaSampler::Unpause();
581       }
582   #endif
583 #endif
584 
585       SetPaused(false);
586     }
587     aWriter.EndArray();
588   }
589   aWriter.End();
590 }
591 
FlushOnJSShutdown(JSContext * aContext)592 void GeckoSampler::FlushOnJSShutdown(JSContext* aContext)
593 {
594 #ifndef SPS_STANDALONE
595   SetPaused(true);
596 
597   {
598     ::MutexAutoLock lock(*sRegisteredThreadsMutex);
599 
600     for (size_t i = 0; i < sRegisteredThreads->size(); i++) {
601       // Thread not being profiled, skip it.
602       if (!sRegisteredThreads->at(i)->Profile() ||
603           sRegisteredThreads->at(i)->IsPendingDelete()) {
604         continue;
605       }
606 
607       // Thread not profiling the context that's going away, skip it.
608       if (sRegisteredThreads->at(i)->Profile()->GetPseudoStack()->mContext != aContext) {
609         continue;
610       }
611 
612       ::MutexAutoLock lock(sRegisteredThreads->at(i)->Profile()->GetMutex());
613       sRegisteredThreads->at(i)->Profile()->FlushSamplesAndMarkers();
614     }
615   }
616 
617   SetPaused(false);
618 #endif
619 }
620 
flushSamplerOnJSShutdown()621 void PseudoStack::flushSamplerOnJSShutdown()
622 {
623 #ifndef SPS_STANDALONE
624   MOZ_ASSERT(mContext);
625   GeckoSampler* t = tlsTicker.get();
626   if (t) {
627     t->FlushOnJSShutdown(mContext);
628   }
629 #endif
630 }
631 
632 // END SaveProfileTask et al
633 ////////////////////////////////////////////////////////////////////////
634 
635 static
addDynamicTag(ThreadProfile & aProfile,char aTagName,const char * aStr)636 void addDynamicTag(ThreadProfile &aProfile, char aTagName, const char *aStr)
637 {
638   aProfile.addTag(ProfileEntry(aTagName, ""));
639   // Add one to store the null termination
640   size_t strLen = strlen(aStr) + 1;
641   for (size_t j = 0; j < strLen;) {
642     // Store as many characters in the void* as the platform allows
643     char text[sizeof(void*)];
644     size_t len = sizeof(void*)/sizeof(char);
645     if (j+len >= strLen) {
646       len = strLen - j;
647     }
648     memcpy(text, &aStr[j], len);
649     j += sizeof(void*)/sizeof(char);
650     // Cast to *((void**) to pass the text data to a void*
651     aProfile.addTag(ProfileEntry('d', *((void**)(&text[0]))));
652   }
653 }
654 
655 static
addPseudoEntry(volatile StackEntry & entry,ThreadProfile & aProfile,PseudoStack * stack,void * lastpc)656 void addPseudoEntry(volatile StackEntry &entry, ThreadProfile &aProfile,
657                     PseudoStack *stack, void *lastpc)
658 {
659   // Pseudo-frames with the BEGIN_PSEUDO_JS flag are just annotations
660   // and should not be recorded in the profile.
661   if (entry.hasFlag(StackEntry::BEGIN_PSEUDO_JS))
662     return;
663 
664   int lineno = -1;
665 
666   // First entry has tagName 's' (start)
667   // Check for magic pointer bit 1 to indicate copy
668   const char* sampleLabel = entry.label();
669   if (entry.isCopyLabel()) {
670     // Store the string using 1 or more 'd' (dynamic) tags
671     // that will happen to the preceding tag
672 
673     addDynamicTag(aProfile, 'c', sampleLabel);
674 #ifndef SPS_STANDALONE
675     if (entry.isJs()) {
676       JSScript* script = entry.script();
677       if (script) {
678         if (!entry.pc()) {
679           // The JIT only allows the top-most entry to have a nullptr pc
680           MOZ_ASSERT(&entry == &stack->mStack[stack->stackSize() - 1]);
681           // If stack-walking was disabled, then that's just unfortunate
682           if (lastpc) {
683             jsbytecode *jspc = js::ProfilingGetPC(stack->mContext, script,
684                                                   lastpc);
685             if (jspc) {
686               lineno = JS_PCToLineNumber(script, jspc);
687             }
688           }
689         } else {
690           lineno = JS_PCToLineNumber(script, entry.pc());
691         }
692       }
693     } else {
694       lineno = entry.line();
695     }
696 #endif
697   } else {
698     aProfile.addTag(ProfileEntry('c', sampleLabel));
699 
700     // XXX: Bug 1010578. Don't assume a CPP entry and try to get the
701     // line for js entries as well.
702     if (entry.isCpp()) {
703       lineno = entry.line();
704     }
705   }
706 
707   if (lineno != -1) {
708     aProfile.addTag(ProfileEntry('n', lineno));
709   }
710 
711   uint32_t category = entry.category();
712   MOZ_ASSERT(!(category & StackEntry::IS_CPP_ENTRY));
713   MOZ_ASSERT(!(category & StackEntry::FRAME_LABEL_COPY));
714 
715   if (category) {
716     aProfile.addTag(ProfileEntry('y', (int)category));
717   }
718 }
719 
720 struct NativeStack
721 {
722   void** pc_array;
723   void** sp_array;
724   size_t size;
725   size_t count;
726 };
727 
728 mozilla::Atomic<bool> WALKING_JS_STACK(false);
729 
730 struct AutoWalkJSStack {
731   bool walkAllowed;
732 
AutoWalkJSStackAutoWalkJSStack733   AutoWalkJSStack() : walkAllowed(false) {
734     walkAllowed = WALKING_JS_STACK.compareExchange(false, true);
735   }
736 
~AutoWalkJSStackAutoWalkJSStack737   ~AutoWalkJSStack() {
738     if (walkAllowed)
739         WALKING_JS_STACK = false;
740   }
741 };
742 
743 static
mergeStacksIntoProfile(ThreadProfile & aProfile,TickSample * aSample,NativeStack & aNativeStack)744 void mergeStacksIntoProfile(ThreadProfile& aProfile, TickSample* aSample, NativeStack& aNativeStack)
745 {
746   PseudoStack* pseudoStack = aProfile.GetPseudoStack();
747   volatile StackEntry *pseudoFrames = pseudoStack->mStack;
748   uint32_t pseudoCount = pseudoStack->stackSize();
749 
750   // Make a copy of the JS stack into a JSFrame array. This is necessary since,
751   // like the native stack, the JS stack is iterated youngest-to-oldest and we
752   // need to iterate oldest-to-youngest when adding entries to aProfile.
753 
754   // Synchronous sampling reports an invalid buffer generation to
755   // ProfilingFrameIterator to avoid incorrectly resetting the generation of
756   // sampled JIT entries inside the JS engine. See note below concerning 'J'
757   // entries.
758   uint32_t startBufferGen;
759   if (aSample->isSamplingCurrentThread) {
760     startBufferGen = UINT32_MAX;
761   } else {
762     startBufferGen = aProfile.bufferGeneration();
763   }
764   uint32_t jsCount = 0;
765 #ifndef SPS_STANDALONE
766   JS::ProfilingFrameIterator::Frame jsFrames[1000];
767   // Only walk jit stack if profiling frame iterator is turned on.
768   if (pseudoStack->mContext && JS::IsProfilingEnabledForContext(pseudoStack->mContext)) {
769     AutoWalkJSStack autoWalkJSStack;
770     const uint32_t maxFrames = mozilla::ArrayLength(jsFrames);
771 
772     if (aSample && autoWalkJSStack.walkAllowed) {
773       JS::ProfilingFrameIterator::RegisterState registerState;
774       registerState.pc = aSample->pc;
775       registerState.sp = aSample->sp;
776 #ifdef ENABLE_ARM_LR_SAVING
777       registerState.lr = aSample->lr;
778 #endif
779 
780       JS::ProfilingFrameIterator jsIter(pseudoStack->mContext,
781                                         registerState,
782                                         startBufferGen);
783       for (; jsCount < maxFrames && !jsIter.done(); ++jsIter) {
784         // See note below regarding 'J' entries.
785         if (aSample->isSamplingCurrentThread || jsIter.isWasm()) {
786           uint32_t extracted = jsIter.extractStack(jsFrames, jsCount, maxFrames);
787           jsCount += extracted;
788           if (jsCount == maxFrames)
789             break;
790         } else {
791           mozilla::Maybe<JS::ProfilingFrameIterator::Frame> frame =
792             jsIter.getPhysicalFrameWithoutLabel();
793           if (frame.isSome())
794             jsFrames[jsCount++] = mozilla::Move(frame.ref());
795         }
796       }
797     }
798   }
799 #endif
800 
801   // Start the sample with a root entry.
802   aProfile.addTag(ProfileEntry('s', "(root)"));
803 
804   // While the pseudo-stack array is ordered oldest-to-youngest, the JS and
805   // native arrays are ordered youngest-to-oldest. We must add frames to
806   // aProfile oldest-to-youngest. Thus, iterate over the pseudo-stack forwards
807   // and JS and native arrays backwards. Note: this means the terminating
808   // condition jsIndex and nativeIndex is being < 0.
809   uint32_t pseudoIndex = 0;
810   int32_t jsIndex = jsCount - 1;
811   int32_t nativeIndex = aNativeStack.count - 1;
812 
813   uint8_t *lastPseudoCppStackAddr = nullptr;
814 
815   // Iterate as long as there is at least one frame remaining.
816   while (pseudoIndex != pseudoCount || jsIndex >= 0 || nativeIndex >= 0) {
817     // There are 1 to 3 frames available. Find and add the oldest.
818 
819     uint8_t *pseudoStackAddr = nullptr;
820     uint8_t *jsStackAddr = nullptr;
821     uint8_t *nativeStackAddr = nullptr;
822 
823     if (pseudoIndex != pseudoCount) {
824       volatile StackEntry &pseudoFrame = pseudoFrames[pseudoIndex];
825 
826       if (pseudoFrame.isCpp())
827         lastPseudoCppStackAddr = (uint8_t *) pseudoFrame.stackAddress();
828 
829 #ifndef SPS_STANDALONE
830       // Skip any pseudo-stack JS frames which are marked isOSR
831       // Pseudostack frames are marked isOSR when the JS interpreter
832       // enters a jit frame on a loop edge (via on-stack-replacement,
833       // or OSR).  To avoid both the pseudoframe and jit frame being
834       // recorded (and showing up twice), the interpreter marks the
835       // interpreter pseudostack entry with the OSR flag to ensure that
836       // it doesn't get counted.
837       if (pseudoFrame.isJs() && pseudoFrame.isOSR()) {
838           pseudoIndex++;
839           continue;
840       }
841 #endif
842 
843       MOZ_ASSERT(lastPseudoCppStackAddr);
844       pseudoStackAddr = lastPseudoCppStackAddr;
845     }
846 
847 #ifndef SPS_STANDALONE
848     if (jsIndex >= 0)
849       jsStackAddr = (uint8_t *) jsFrames[jsIndex].stackAddress;
850 #endif
851 
852     if (nativeIndex >= 0)
853       nativeStackAddr = (uint8_t *) aNativeStack.sp_array[nativeIndex];
854 
855     // If there's a native stack entry which has the same SP as a
856     // pseudo stack entry, pretend we didn't see the native stack
857     // entry.  Ditto for a native stack entry which has the same SP as
858     // a JS stack entry.  In effect this means pseudo or JS entries
859     // trump conflicting native entries.
860     if (nativeStackAddr && (pseudoStackAddr == nativeStackAddr || jsStackAddr == nativeStackAddr)) {
861       nativeStackAddr = nullptr;
862       nativeIndex--;
863       MOZ_ASSERT(pseudoStackAddr || jsStackAddr);
864     }
865 
866     // Sanity checks.
867     MOZ_ASSERT_IF(pseudoStackAddr, pseudoStackAddr != jsStackAddr &&
868                                    pseudoStackAddr != nativeStackAddr);
869     MOZ_ASSERT_IF(jsStackAddr, jsStackAddr != pseudoStackAddr &&
870                                jsStackAddr != nativeStackAddr);
871     MOZ_ASSERT_IF(nativeStackAddr, nativeStackAddr != pseudoStackAddr &&
872                                    nativeStackAddr != jsStackAddr);
873 
874     // Check to see if pseudoStack frame is top-most.
875     if (pseudoStackAddr > jsStackAddr && pseudoStackAddr > nativeStackAddr) {
876       MOZ_ASSERT(pseudoIndex < pseudoCount);
877       volatile StackEntry &pseudoFrame = pseudoFrames[pseudoIndex];
878       addPseudoEntry(pseudoFrame, aProfile, pseudoStack, nullptr);
879       pseudoIndex++;
880       continue;
881     }
882 
883 #ifndef SPS_STANDALONE
884     // Check to see if JS jit stack frame is top-most
885     if (jsStackAddr > nativeStackAddr) {
886       MOZ_ASSERT(jsIndex >= 0);
887       const JS::ProfilingFrameIterator::Frame& jsFrame = jsFrames[jsIndex];
888 
889       // Stringifying non-wasm JIT frames is delayed until streaming
890       // time. To re-lookup the entry in the JitcodeGlobalTable, we need to
891       // store the JIT code address ('J') in the circular buffer.
892       //
893       // Note that we cannot do this when we are sychronously sampling the
894       // current thread; that is, when called from profiler_get_backtrace. The
895       // captured backtrace is usually externally stored for an indeterminate
896       // amount of time, such as in nsRefreshDriver. Problematically, the
897       // stored backtrace may be alive across a GC during which the profiler
898       // itself is disabled. In that case, the JS engine is free to discard
899       // its JIT code. This means that if we inserted such 'J' entries into
900       // the buffer, nsRefreshDriver would now be holding on to a backtrace
901       // with stale JIT code return addresses.
902       if (aSample->isSamplingCurrentThread ||
903           jsFrame.kind == JS::ProfilingFrameIterator::Frame_Wasm) {
904         addDynamicTag(aProfile, 'c', jsFrame.label.get());
905       } else {
906         MOZ_ASSERT(jsFrame.kind == JS::ProfilingFrameIterator::Frame_Ion ||
907                    jsFrame.kind == JS::ProfilingFrameIterator::Frame_Baseline);
908         aProfile.addTag(ProfileEntry('J', jsFrames[jsIndex].returnAddress));
909       }
910 
911       jsIndex--;
912       continue;
913     }
914 #endif
915 
916     // If we reach here, there must be a native stack entry and it must be the
917     // greatest entry.
918     if (nativeStackAddr) {
919       MOZ_ASSERT(nativeIndex >= 0);
920       aProfile
921         .addTag(ProfileEntry('l', (void*)aNativeStack.pc_array[nativeIndex]));
922     }
923     if (nativeIndex >= 0) {
924       nativeIndex--;
925     }
926   }
927 
928 #ifndef SPS_STANDALONE
929   // Update the JS context with the current profile sample buffer generation.
930   //
931   // Do not do this for synchronous sampling, which create their own
932   // ProfileBuffers.
933   if (!aSample->isSamplingCurrentThread && pseudoStack->mContext) {
934     MOZ_ASSERT(aProfile.bufferGeneration() >= startBufferGen);
935     uint32_t lapCount = aProfile.bufferGeneration() - startBufferGen;
936     JS::UpdateJSContextProfilerSampleBufferGen(pseudoStack->mContext,
937                                                aProfile.bufferGeneration(),
938                                                lapCount);
939   }
940 #endif
941 }
942 
943 #ifdef USE_NS_STACKWALK
944 static
StackWalkCallback(uint32_t aFrameNumber,void * aPC,void * aSP,void * aClosure)945 void StackWalkCallback(uint32_t aFrameNumber, void* aPC, void* aSP,
946                        void* aClosure)
947 {
948   NativeStack* nativeStack = static_cast<NativeStack*>(aClosure);
949   MOZ_ASSERT(nativeStack->count < nativeStack->size);
950   nativeStack->sp_array[nativeStack->count] = aSP;
951   nativeStack->pc_array[nativeStack->count] = aPC;
952   nativeStack->count++;
953 }
954 
doNativeBacktrace(ThreadProfile & aProfile,TickSample * aSample)955 void GeckoSampler::doNativeBacktrace(ThreadProfile &aProfile, TickSample* aSample)
956 {
957   void* pc_array[1000];
958   void* sp_array[1000];
959   NativeStack nativeStack = {
960     pc_array,
961     sp_array,
962     mozilla::ArrayLength(pc_array),
963     0
964   };
965 
966   // Start with the current function. We use 0 as the frame number here because
967   // the FramePointerStackWalk() and MozStackWalk() calls below will use 1..N.
968   // This is a bit weird but it doesn't matter because StackWalkCallback()
969   // doesn't use the frame number argument.
970   StackWalkCallback(/* frameNumber */ 0, aSample->pc, aSample->sp, &nativeStack);
971 
972   uint32_t maxFrames = uint32_t(nativeStack.size - nativeStack.count);
973   // win X64 doesn't support disabling frame pointers emission so we need
974   // to fallback to using StackWalk64 which is slower.
975 #if defined(XP_MACOSX) || (defined(XP_WIN) && !defined(V8_HOST_ARCH_X64))
976   void *stackEnd = aSample->threadProfile->GetStackTop();
977   bool rv = true;
978   if (aSample->fp >= aSample->sp && aSample->fp <= stackEnd)
979     rv = FramePointerStackWalk(StackWalkCallback, /* skipFrames */ 0,
980                                maxFrames, &nativeStack,
981                                reinterpret_cast<void**>(aSample->fp), stackEnd);
982 #else
983   void *platformData = nullptr;
984 
985   uintptr_t thread = GetThreadHandle(aSample->threadProfile->GetPlatformData());
986   MOZ_ASSERT(thread);
987   bool rv = MozStackWalk(StackWalkCallback, /* skipFrames */ 0, maxFrames,
988                              &nativeStack, thread, platformData);
989 #endif
990   if (rv)
991     mergeStacksIntoProfile(aProfile, aSample, nativeStack);
992 }
993 #endif
994 
995 
996 #ifdef USE_EHABI_STACKWALK
doNativeBacktrace(ThreadProfile & aProfile,TickSample * aSample)997 void GeckoSampler::doNativeBacktrace(ThreadProfile &aProfile, TickSample* aSample)
998 {
999   void *pc_array[1000];
1000   void *sp_array[1000];
1001   NativeStack nativeStack = {
1002     pc_array,
1003     sp_array,
1004     mozilla::ArrayLength(pc_array),
1005     0
1006   };
1007 
1008   const mcontext_t *mcontext = &reinterpret_cast<ucontext_t *>(aSample->context)->uc_mcontext;
1009   mcontext_t savedContext;
1010   PseudoStack *pseudoStack = aProfile.GetPseudoStack();
1011 
1012   nativeStack.count = 0;
1013   // The pseudostack contains an "EnterJIT" frame whenever we enter
1014   // JIT code with profiling enabled; the stack pointer value points
1015   // the saved registers.  We use this to unwind resume unwinding
1016   // after encounting JIT code.
1017   for (uint32_t i = pseudoStack->stackSize(); i > 0; --i) {
1018     // The pseudostack grows towards higher indices, so we iterate
1019     // backwards (from callee to caller).
1020     volatile StackEntry &entry = pseudoStack->mStack[i - 1];
1021     if (!entry.isJs() && strcmp(entry.label(), "EnterJIT") == 0) {
1022       // Found JIT entry frame.  Unwind up to that point (i.e., force
1023       // the stack walk to stop before the block of saved registers;
1024       // note that it yields nondecreasing stack pointers), then restore
1025       // the saved state.
1026       uint32_t *vSP = reinterpret_cast<uint32_t*>(entry.stackAddress());
1027 
1028       nativeStack.count += EHABIStackWalk(*mcontext,
1029                                           /* stackBase = */ vSP,
1030                                           sp_array + nativeStack.count,
1031                                           pc_array + nativeStack.count,
1032                                           nativeStack.size - nativeStack.count);
1033 
1034       memset(&savedContext, 0, sizeof(savedContext));
1035       // See also: struct EnterJITStack in js/src/jit/arm/Trampoline-arm.cpp
1036       savedContext.arm_r4 = *vSP++;
1037       savedContext.arm_r5 = *vSP++;
1038       savedContext.arm_r6 = *vSP++;
1039       savedContext.arm_r7 = *vSP++;
1040       savedContext.arm_r8 = *vSP++;
1041       savedContext.arm_r9 = *vSP++;
1042       savedContext.arm_r10 = *vSP++;
1043       savedContext.arm_fp = *vSP++;
1044       savedContext.arm_lr = *vSP++;
1045       savedContext.arm_sp = reinterpret_cast<uint32_t>(vSP);
1046       savedContext.arm_pc = savedContext.arm_lr;
1047       mcontext = &savedContext;
1048     }
1049   }
1050 
1051   // Now unwind whatever's left (starting from either the last EnterJIT
1052   // frame or, if no EnterJIT was found, the original registers).
1053   nativeStack.count += EHABIStackWalk(*mcontext,
1054                                       aProfile.GetStackTop(),
1055                                       sp_array + nativeStack.count,
1056                                       pc_array + nativeStack.count,
1057                                       nativeStack.size - nativeStack.count);
1058 
1059   mergeStacksIntoProfile(aProfile, aSample, nativeStack);
1060 }
1061 #endif
1062 
1063 
1064 #ifdef USE_LUL_STACKWALK
doNativeBacktrace(ThreadProfile & aProfile,TickSample * aSample)1065 void GeckoSampler::doNativeBacktrace(ThreadProfile &aProfile, TickSample* aSample)
1066 {
1067   const mcontext_t* mc
1068     = &reinterpret_cast<ucontext_t *>(aSample->context)->uc_mcontext;
1069 
1070   lul::UnwindRegs startRegs;
1071   memset(&startRegs, 0, sizeof(startRegs));
1072 
1073 # if defined(SPS_PLAT_amd64_linux)
1074   startRegs.xip = lul::TaggedUWord(mc->gregs[REG_RIP]);
1075   startRegs.xsp = lul::TaggedUWord(mc->gregs[REG_RSP]);
1076   startRegs.xbp = lul::TaggedUWord(mc->gregs[REG_RBP]);
1077 # elif defined(SPS_PLAT_arm_android)
1078   startRegs.r15 = lul::TaggedUWord(mc->arm_pc);
1079   startRegs.r14 = lul::TaggedUWord(mc->arm_lr);
1080   startRegs.r13 = lul::TaggedUWord(mc->arm_sp);
1081   startRegs.r12 = lul::TaggedUWord(mc->arm_ip);
1082   startRegs.r11 = lul::TaggedUWord(mc->arm_fp);
1083   startRegs.r7  = lul::TaggedUWord(mc->arm_r7);
1084 # elif defined(SPS_PLAT_x86_linux) || defined(SPS_PLAT_x86_android)
1085   startRegs.xip = lul::TaggedUWord(mc->gregs[REG_EIP]);
1086   startRegs.xsp = lul::TaggedUWord(mc->gregs[REG_ESP]);
1087   startRegs.xbp = lul::TaggedUWord(mc->gregs[REG_EBP]);
1088 # else
1089 #   error "Unknown plat"
1090 # endif
1091 
1092   /* Copy up to N_STACK_BYTES from rsp-REDZONE upwards, but not
1093      going past the stack's registered top point.  Do some basic
1094      sanity checks too.  This assumes that the TaggedUWord holding
1095      the stack pointer value is valid, but it should be, since it
1096      was constructed that way in the code just above. */
1097 
1098   lul::StackImage stackImg;
1099 
1100   {
1101 #   if defined(SPS_PLAT_amd64_linux)
1102     uintptr_t rEDZONE_SIZE = 128;
1103     uintptr_t start = startRegs.xsp.Value() - rEDZONE_SIZE;
1104 #   elif defined(SPS_PLAT_arm_android)
1105     uintptr_t rEDZONE_SIZE = 0;
1106     uintptr_t start = startRegs.r13.Value() - rEDZONE_SIZE;
1107 #   elif defined(SPS_PLAT_x86_linux) || defined(SPS_PLAT_x86_android)
1108     uintptr_t rEDZONE_SIZE = 0;
1109     uintptr_t start = startRegs.xsp.Value() - rEDZONE_SIZE;
1110 #   else
1111 #     error "Unknown plat"
1112 #   endif
1113     uintptr_t end   = reinterpret_cast<uintptr_t>(aProfile.GetStackTop());
1114     uintptr_t ws    = sizeof(void*);
1115     start &= ~(ws-1);
1116     end   &= ~(ws-1);
1117     uintptr_t nToCopy = 0;
1118     if (start < end) {
1119       nToCopy = end - start;
1120       if (nToCopy > lul::N_STACK_BYTES)
1121         nToCopy = lul::N_STACK_BYTES;
1122     }
1123     MOZ_ASSERT(nToCopy <= lul::N_STACK_BYTES);
1124     stackImg.mLen       = nToCopy;
1125     stackImg.mStartAvma = start;
1126     if (nToCopy > 0) {
1127       memcpy(&stackImg.mContents[0], (void*)start, nToCopy);
1128       (void)VALGRIND_MAKE_MEM_DEFINED(&stackImg.mContents[0], nToCopy);
1129     }
1130   }
1131 
1132   // The maximum number of frames that LUL will produce.  Setting it
1133   // too high gives a risk of it wasting a lot of time looping on
1134   // corrupted stacks.
1135   const int MAX_NATIVE_FRAMES = 256;
1136 
1137   size_t scannedFramesAllowed = 0;
1138 
1139   uintptr_t framePCs[MAX_NATIVE_FRAMES];
1140   uintptr_t frameSPs[MAX_NATIVE_FRAMES];
1141   size_t framesAvail = mozilla::ArrayLength(framePCs);
1142   size_t framesUsed  = 0;
1143   size_t scannedFramesAcquired = 0;
1144   sLUL->Unwind( &framePCs[0], &frameSPs[0],
1145                 &framesUsed, &scannedFramesAcquired,
1146                 framesAvail, scannedFramesAllowed,
1147                 &startRegs, &stackImg );
1148 
1149   NativeStack nativeStack = {
1150     reinterpret_cast<void**>(framePCs),
1151     reinterpret_cast<void**>(frameSPs),
1152     mozilla::ArrayLength(framePCs),
1153     0
1154   };
1155 
1156   nativeStack.count = framesUsed;
1157 
1158   mergeStacksIntoProfile(aProfile, aSample, nativeStack);
1159 
1160   // Update stats in the LUL stats object.  Unfortunately this requires
1161   // three global memory operations.
1162   sLUL->mStats.mContext += 1;
1163   sLUL->mStats.mCFI     += framesUsed - 1 - scannedFramesAcquired;
1164   sLUL->mStats.mScanned += scannedFramesAcquired;
1165 }
1166 #endif
1167 
1168 
1169 static
doSampleStackTrace(ThreadProfile & aProfile,TickSample * aSample,bool aAddLeafAddresses)1170 void doSampleStackTrace(ThreadProfile &aProfile, TickSample *aSample, bool aAddLeafAddresses)
1171 {
1172   NativeStack nativeStack = { nullptr, nullptr, 0, 0 };
1173   mergeStacksIntoProfile(aProfile, aSample, nativeStack);
1174 
1175 #ifdef ENABLE_SPS_LEAF_DATA
1176   if (aSample && aAddLeafAddresses) {
1177     aProfile.addTag(ProfileEntry('l', (void*)aSample->pc));
1178 #ifdef ENABLE_ARM_LR_SAVING
1179     aProfile.addTag(ProfileEntry('L', (void*)aSample->lr));
1180 #endif
1181   }
1182 #endif
1183 }
1184 
Tick(TickSample * sample)1185 void GeckoSampler::Tick(TickSample* sample)
1186 {
1187   // Don't allow for ticks to happen within other ticks.
1188   InplaceTick(sample);
1189 }
1190 
InplaceTick(TickSample * sample)1191 void GeckoSampler::InplaceTick(TickSample* sample)
1192 {
1193   ThreadProfile& currThreadProfile = *sample->threadProfile;
1194 
1195   currThreadProfile.addTag(ProfileEntry('T', currThreadProfile.ThreadId()));
1196 
1197   if (sample) {
1198     mozilla::TimeDuration delta = sample->timestamp - sStartTime;
1199     currThreadProfile.addTag(ProfileEntry('t', delta.ToMilliseconds()));
1200   }
1201 
1202   PseudoStack* stack = currThreadProfile.GetPseudoStack();
1203 
1204 #if defined(USE_NS_STACKWALK) || defined(USE_EHABI_STACKWALK) || \
1205     defined(USE_LUL_STACKWALK)
1206   if (mUseStackWalk) {
1207     doNativeBacktrace(currThreadProfile, sample);
1208   } else {
1209     doSampleStackTrace(currThreadProfile, sample, mAddLeafAddresses);
1210   }
1211 #else
1212   doSampleStackTrace(currThreadProfile, sample, mAddLeafAddresses);
1213 #endif
1214 
1215   // Don't process the PeudoStack's markers if we're
1216   // synchronously sampling the current thread.
1217   if (!sample->isSamplingCurrentThread) {
1218     ProfilerMarkerLinkedList* pendingMarkersList = stack->getPendingMarkers();
1219     while (pendingMarkersList && pendingMarkersList->peek()) {
1220       ProfilerMarker* marker = pendingMarkersList->popHead();
1221       currThreadProfile.addStoredMarker(marker);
1222       currThreadProfile.addTag(ProfileEntry('m', marker));
1223     }
1224   }
1225 
1226 #ifndef SPS_STANDALONE
1227   if (sample && currThreadProfile.GetThreadResponsiveness()->HasData()) {
1228     mozilla::TimeDuration delta = currThreadProfile.GetThreadResponsiveness()->GetUnresponsiveDuration(sample->timestamp);
1229     currThreadProfile.addTag(ProfileEntry('r', delta.ToMilliseconds()));
1230   }
1231 #endif
1232 
1233   // rssMemory is equal to 0 when we are not recording.
1234   if (sample && sample->rssMemory != 0) {
1235     currThreadProfile.addTag(ProfileEntry('R', static_cast<double>(sample->rssMemory)));
1236   }
1237 
1238   // ussMemory is equal to 0 when we are not recording.
1239   if (sample && sample->ussMemory != 0) {
1240     currThreadProfile.addTag(ProfileEntry('U', static_cast<double>(sample->ussMemory)));
1241   }
1242 
1243 #if defined(XP_WIN)
1244   if (mProfilePower) {
1245     mIntelPowerGadget->TakeSample();
1246     currThreadProfile.addTag(ProfileEntry('p', static_cast<double>(mIntelPowerGadget->GetTotalPackagePowerInWatts())));
1247   }
1248 #endif
1249 
1250   if (sLastFrameNumber != sFrameNumber) {
1251     currThreadProfile.addTag(ProfileEntry('f', sFrameNumber));
1252     sLastFrameNumber = sFrameNumber;
1253   }
1254 }
1255 
1256 namespace {
1257 
NewSyncProfile()1258 SyncProfile* NewSyncProfile()
1259 {
1260   PseudoStack* stack = tlsPseudoStack.get();
1261   if (!stack) {
1262     MOZ_ASSERT(stack);
1263     return nullptr;
1264   }
1265   Thread::tid_t tid = Thread::GetCurrentId();
1266 
1267   ThreadInfo* info = new ThreadInfo("SyncProfile", tid, false, stack, nullptr);
1268   SyncProfile* profile = new SyncProfile(info, GET_BACKTRACE_DEFAULT_ENTRY);
1269   return profile;
1270 }
1271 
1272 } // namespace
1273 
GetBacktrace()1274 SyncProfile* GeckoSampler::GetBacktrace()
1275 {
1276   SyncProfile* profile = NewSyncProfile();
1277 
1278   TickSample sample;
1279   sample.threadProfile = profile;
1280 
1281 #if defined(HAVE_NATIVE_UNWIND) || defined(USE_LUL_STACKWALK)
1282 #if defined(XP_WIN) || defined(LINUX)
1283   tickcontext_t context;
1284   sample.PopulateContext(&context);
1285 #elif defined(XP_MACOSX)
1286   sample.PopulateContext(nullptr);
1287 #endif
1288 #endif
1289 
1290   sample.isSamplingCurrentThread = true;
1291   sample.timestamp = mozilla::TimeStamp::Now();
1292 
1293   profile->BeginUnwind();
1294   Tick(&sample);
1295   profile->EndUnwind();
1296 
1297   return profile;
1298 }
1299 
1300 void
GetBufferInfo(uint32_t * aCurrentPosition,uint32_t * aTotalSize,uint32_t * aGeneration)1301 GeckoSampler::GetBufferInfo(uint32_t *aCurrentPosition, uint32_t *aTotalSize, uint32_t *aGeneration)
1302 {
1303   *aCurrentPosition = mBuffer->mWritePos;
1304   *aTotalSize = mBuffer->mEntrySize;
1305   *aGeneration = mBuffer->mGeneration;
1306 }
1307