LCOV - code coverage report
Current view: top level - toolkit/crashreporter - ThreadAnnotation.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 7 84 8.3 %
Date: 2017-07-14 16:53:18 Functions: 1 14 7.1 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* This Source Code Form is subject to the terms of the Mozilla Public
       2             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       3             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       4             : 
       5             : #include "ThreadAnnotation.h"
       6             : 
       7             : #include <stddef.h>
       8             : 
       9             : #include "mozilla/Assertions.h"
      10             : #include "mozilla/StaticMutex.h"
      11             : #include "mozilla/TypeTraits.h"
      12             : #include "mozilla/UniquePtr.h"
      13             : 
      14             : #include "prthread.h"
      15             : #include "nsDebug.h"
      16             : #include "nsExceptionHandler.h"
      17             : #include "nsString.h"
      18             : #include "nsTArray.h"
      19             : 
      20             : using mozilla::StaticMutex;
      21             : using mozilla::StaticMutexAutoLock;
      22             : using mozilla::UniquePtr;
      23             : 
      24             : namespace CrashReporter {
      25             : 
      26             : namespace {
      27             : 
      28             : // Protects access to sInitialized and sThreadAnnotations.
      29           3 : static StaticMutex sMutex;
      30             : 
      31             : class ThreadAnnotationSpan {
      32             : public:
      33           0 :   ThreadAnnotationSpan(uint32_t aBegin, uint32_t aEnd)
      34           0 :     : mBegin(aBegin)
      35           0 :     , mEnd(aEnd)
      36             :   {
      37           0 :     MOZ_ASSERT(mBegin < mEnd);
      38           0 :   }
      39             : 
      40             :   ~ThreadAnnotationSpan();
      41             : 
      42             :   class Comparator {
      43             :   public:
      44           0 :      bool Equals(const ThreadAnnotationSpan* const& a,
      45             :                  const ThreadAnnotationSpan* const& b) const
      46             :      {
      47           0 :        return a->mBegin == b->mBegin;
      48             :      }
      49             : 
      50           0 :      bool LessThan(const ThreadAnnotationSpan* const& a,
      51             :                    const ThreadAnnotationSpan* const& b) const
      52             :      {
      53           0 :        return a->mBegin < b->mBegin;
      54             :      }
      55             :   };
      56             : 
      57             : private:
      58             :   // ~ThreadAnnotationSpan() does nontrivial thing. Make sure we don't
      59             :   // instantiate accidentally.
      60             :   ThreadAnnotationSpan(const ThreadAnnotationSpan& aOther) = delete;
      61             :   ThreadAnnotationSpan& operator=(const ThreadAnnotationSpan& aOther) = delete;
      62             : 
      63             :   friend class ThreadAnnotationData;
      64             :   friend class Comparator;
      65             : 
      66             :   uint32_t mBegin;
      67             :   uint32_t mEnd;
      68             : };
      69             : 
      70             : // This class keeps the flat version of thread annotations for each thread.
      71             : // When a thread calls CrashReporter::SetCurrentThreadName(), it adds
      72             : // information about the calling thread (thread id and name) to this class.
      73             : // When crash happens, the crash reporter gets flat representation and add to
      74             : // the crash annotation file.
      75             : class ThreadAnnotationData {
      76             : public:
      77           0 :   ThreadAnnotationData()
      78           0 :   {}
      79             : 
      80           0 :   ~ThreadAnnotationData()
      81           0 :   {}
      82             : 
      83             :   // Adds <pre> tid:"thread name",</pre> annotation to the current annotations.
      84             :   // Returns an instance of ThreadAnnotationSpan for cleanup on thread
      85             :   // termination.
      86             :   ThreadAnnotationSpan*
      87           0 :   AddThreadAnnotation(ThreadId aTid, const char* aThreadName)
      88             :   {
      89           0 :     if (!aTid || !aThreadName) {
      90           0 :       return nullptr;
      91             :     }
      92             : 
      93           0 :     uint32_t oldLength = mData.Length();
      94           0 :     mData.AppendPrintf("%u:\"%s\",", aTid, aThreadName);
      95           0 :     uint32_t newLength = mData.Length();
      96             : 
      97           0 :     ThreadAnnotationSpan* rv = new ThreadAnnotationSpan(oldLength, newLength);
      98           0 :     mDataSpans.AppendElement(rv);
      99           0 :     return rv;
     100             :   }
     101             : 
     102             :   // Called on thread termination. Removes the thread annotation, represented as
     103             :   // ThreadAnnotationSpan, from the flat representation.
     104           0 :   void EraseThreadAnnotation(const ThreadAnnotationSpan& aThreadInfo)
     105             :   {
     106           0 :     uint32_t begin = aThreadInfo.mBegin;
     107           0 :     uint32_t end = aThreadInfo.mEnd;
     108             : 
     109           0 :     if (!(begin < end &&
     110           0 :           end <= mData.Length())) {
     111           0 :       return;
     112             :     }
     113             : 
     114           0 :     uint32_t cutLength = end - begin;
     115           0 :     mData.Cut(begin, cutLength);
     116             : 
     117             :     // Adjust the ThreadAnnotationSpan affected by data shifting.
     118           0 :     size_t index = mDataSpans.BinaryIndexOf(&aThreadInfo,
     119           0 :                                             ThreadAnnotationSpan::Comparator());
     120           0 :     for (size_t i = index + 1; i < mDataSpans.Length(); i++) {
     121           0 :       ThreadAnnotationSpan* elem = mDataSpans[i];
     122             : 
     123           0 :       MOZ_ASSERT(elem->mBegin >= cutLength);
     124           0 :       MOZ_ASSERT(elem->mEnd > cutLength);
     125             : 
     126           0 :       elem->mBegin -= cutLength;
     127           0 :       elem->mEnd -= cutLength;
     128             :     }
     129             : 
     130             :     // No loner tracking aThreadInfo.
     131           0 :     mDataSpans.RemoveElementAt(index);
     132             :   }
     133             : 
     134             :   // Gets the flat representation of thread annotations.
     135           0 :   void GetData(const std::function<void(const char*)>& aCallback)
     136             :   {
     137           0 :     aCallback(mData.BeginReading());
     138           0 :   }
     139             : private:
     140             :   // The flat representation of thread annotations.
     141             :   nsCString mData;
     142             : 
     143             :   // This array tracks the created ThreadAnnotationSpan instances so that we
     144             :   // can make adjustments accordingly when we cut substrings from mData on
     145             :   // thread exit.
     146             :   nsTArray<ThreadAnnotationSpan*> mDataSpans;
     147             : };
     148             : 
     149             : template<typename T>
     150             : class DeleteWithLock
     151             : {
     152             : public:
     153             :   constexpr DeleteWithLock() {}
     154             : 
     155             :   void operator()(T* aPtr) const
     156             :   {
     157             :     static_assert(sizeof(T) > 0, "T must be complete");
     158             :     StaticMutexAutoLock lock(sMutex);
     159             : 
     160             :     delete aPtr;
     161             :   }
     162             : };
     163             : 
     164             : static bool sInitialized = false;
     165           3 : static UniquePtr<ThreadAnnotationData> sThreadAnnotations;
     166             : 
     167             : static unsigned sTLSThreadInfoKey = (unsigned) -1;
     168           0 : void ThreadLocalDestructor(void* aUserData)
     169             : {
     170           0 :   MOZ_ASSERT(aUserData);
     171             : 
     172           0 :   StaticMutexAutoLock lock(sMutex);
     173             : 
     174             :   ThreadAnnotationSpan* aThreadInfo =
     175           0 :     static_cast<ThreadAnnotationSpan*>(aUserData);
     176           0 :   delete aThreadInfo;
     177           0 : }
     178             : 
     179             : // This is called on thread termination.
     180           0 : ThreadAnnotationSpan::~ThreadAnnotationSpan()
     181             : {
     182             :   // Note that we can't lock the mutex here because this function may be called
     183             :   // from SetCurrentThreadName().
     184           0 :   sMutex.AssertCurrentThreadOwns();
     185             : 
     186           0 :   if (sThreadAnnotations) {
     187           0 :     sThreadAnnotations->EraseThreadAnnotation(*this);
     188             :   }
     189           0 : }
     190             : 
     191             : } // Anonymous namespace.
     192             : 
     193           0 : void InitThreadAnnotation()
     194             : {
     195           0 :   StaticMutexAutoLock lock(sMutex);
     196             : 
     197           0 :   if (sInitialized) {
     198           0 :     return;
     199             :   }
     200             : 
     201             :   PRStatus status = PR_NewThreadPrivateIndex(&sTLSThreadInfoKey,
     202           0 :                                              &ThreadLocalDestructor);
     203           0 :   if (status == PR_FAILURE) {
     204           0 :     return;
     205             :   }
     206             : 
     207           0 :   sInitialized = true;
     208             : 
     209           0 :   sThreadAnnotations = mozilla::MakeUnique<ThreadAnnotationData>();
     210             : }
     211             : 
     212          76 : void SetCurrentThreadName(const char* aName)
     213             : {
     214          76 :   if (PR_GetThreadPrivate(sTLSThreadInfoKey)) {
     215             :     // Explicitly set TLS value to null (and call the dtor function ) before
     216             :     // acquiring sMutex to avoid reentrant deadlock.
     217           0 :     PR_SetThreadPrivate(sTLSThreadInfoKey, nullptr);
     218             :   }
     219             : 
     220          76 :   StaticMutexAutoLock lock(sMutex);
     221             : 
     222          76 :   if (!sInitialized) {
     223          76 :     return;
     224             :   }
     225             : 
     226             :   ThreadAnnotationSpan* threadInfo =
     227           0 :     sThreadAnnotations->AddThreadAnnotation(CurrentThreadId(),
     228           0 :                                             aName);
     229             :   // This may destroy the old insatnce.
     230           0 :   PR_SetThreadPrivate(sTLSThreadInfoKey, threadInfo);
     231             : }
     232             : 
     233           0 : void GetFlatThreadAnnotation(const std::function<void(const char*)>& aCallback)
     234             : {
     235           0 :   StaticMutexAutoLock lock(sMutex);
     236             : 
     237           0 :   if (sThreadAnnotations) {
     238           0 :     sThreadAnnotations->GetData(aCallback);
     239             :   } else {
     240             :     // Maybe already shutdown: call aCallback with empty annotation data.
     241           0 :     aCallback("");
     242             :   }
     243           0 : }
     244             : 
     245           0 : void ShutdownThreadAnnotation()
     246             : {
     247           0 :   StaticMutexAutoLock lock(sMutex);
     248             : 
     249           0 :   sInitialized = false;
     250           0 :   sThreadAnnotations.reset();
     251           0 : }
     252             : 
     253             : }

Generated by: LCOV version 1.13