Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/xpcom/base/CodeAddressService.h
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* vim: set ts=8 sts=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
5
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7
#ifndef CodeAddressService_h__
8
#define CodeAddressService_h__
9
10
#include "mozilla/Assertions.h"
11
#include "mozilla/HashFunctions.h"
12
#include "mozilla/IntegerPrintfMacros.h"
13
#include "mozilla/MemoryReporting.h"
14
#include "mozilla/Types.h"
15
16
#include "mozilla/StackWalk.h"
17
18
namespace mozilla {
19
20
// This class is used to print details about code locations.
21
//
22
// |StringTable| must implement an Intern() method that returns an interned
23
// copy of the string that was passed in, as well as a standard
24
// SizeOfExcludingThis() method.
25
//
26
// |StringAlloc| must implement |copy| and |free|. |copy| copies a string,
27
// while |free| is used to free strings created by |copy|.
28
//
29
// |DescribeCodeAddressLock| is needed when the callers may be holding a lock
30
// used by MozDescribeCodeAddress.  |DescribeCodeAddressLock| must implement
31
// static methods IsLocked(), Unlock() and Lock().
32
template <class StringTable,
33
          class StringAlloc,
34
          class DescribeCodeAddressLock>
35
class CodeAddressService
36
{
37
  // GetLocation() is the key function in this class.  It's basically a wrapper
38
  // around MozDescribeCodeAddress.
39
  //
40
  // However, MozDescribeCodeAddress is very slow on some platforms, and we
41
  // have lots of repeated (i.e. same PC) calls to it.  So we do some caching
42
  // of results.  Each cached result includes two strings (|mFunction| and
43
  // |mLibrary|), so we also optimize them for space in the following ways.
44
  //
45
  // - The number of distinct library names is small, e.g. a few dozen.  There
46
  //   is lots of repetition, especially of libxul.  So we intern them in their
47
  //   own table, which saves space over duplicating them for each cache entry.
48
  //
49
  // - The number of distinct function names is much higher, so we duplicate
50
  //   them in each cache entry.  That's more space-efficient than interning
51
  //   because entries containing single-occurrence function names are quickly
52
  //   overwritten, and their copies released.  In addition, empty function
53
  //   names are common, so we use nullptr to represent them compactly.
54
55
  StringTable mLibraryStrings;
56
57
  struct Entry
58
  {
59
    const void* mPc;
60
    char*       mFunction;  // owned by the Entry;  may be null
61
    const char* mLibrary;   // owned by mLibraryStrings;  never null
62
                            //   in a non-empty entry is in use
63
    ptrdiff_t   mLOffset;
64
    char*       mFileName;  // owned by the Entry; may be null
65
    uint32_t    mLineNo:31;
66
    uint32_t    mInUse:1;   // is the entry used?
67
68
    Entry()
69
      : mPc(0), mFunction(nullptr), mLibrary(nullptr), mLOffset(0),
70
        mFileName(nullptr), mLineNo(0), mInUse(0)
71
0
    {}
72
73
    ~Entry()
74
0
    {
75
0
      // We don't free mLibrary because it's externally owned.
76
0
      StringAlloc::free(mFunction);
77
0
      StringAlloc::free(mFileName);
78
0
    }
79
80
    void Replace(const void* aPc, const char* aFunction,
81
                 const char* aLibrary, ptrdiff_t aLOffset,
82
                 const char* aFileName, unsigned long aLineNo)
83
0
    {
84
0
      mPc = aPc;
85
0
86
0
      // Convert "" to nullptr.  Otherwise, make a copy of the name.
87
0
      StringAlloc::free(mFunction);
88
0
      mFunction = !aFunction[0] ? nullptr : StringAlloc::copy(aFunction);
89
0
      StringAlloc::free(mFileName);
90
0
      mFileName = !aFileName[0] ? nullptr : StringAlloc::copy(aFileName);
91
0
92
0
      mLibrary = aLibrary;
93
0
      mLOffset = aLOffset;
94
0
      mLineNo = aLineNo;
95
0
96
0
      mInUse = 1;
97
0
    }
98
99
    size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
100
    {
101
      // Don't measure mLibrary because it's externally owned.
102
      size_t n = 0;
103
      n += aMallocSizeOf(mFunction);
104
      n += aMallocSizeOf(mFileName);
105
      return n;
106
    }
107
  };
108
109
  // A direct-mapped cache.  When doing dmd::Analyze() just after starting
110
  // desktop Firefox (which is similar to analyzing after a longer-running
111
  // session, thanks to the limit on how many records we print), a cache with
112
  // 2^24 entries (which approximates an infinite-entry cache) has a ~91% hit
113
  // rate.  A cache with 2^12 entries has a ~83% hit rate, and takes up ~85 KiB
114
  // (on 32-bit platforms) or ~150 KiB (on 64-bit platforms).
115
  static const size_t kNumEntries = 1 << 12;
116
  static const size_t kMask = kNumEntries - 1;
117
  Entry mEntries[kNumEntries];
118
119
  size_t mNumCacheHits;
120
  size_t mNumCacheMisses;
121
122
public:
123
  CodeAddressService()
124
    : mEntries(), mNumCacheHits(0), mNumCacheMisses(0)
125
0
  {
126
0
  }
127
128
  void GetLocation(uint32_t aFrameNumber, const void* aPc, char* aBuf,
129
                   size_t aBufLen)
130
0
  {
131
0
    MOZ_ASSERT(DescribeCodeAddressLock::IsLocked());
132
0
133
0
    uint32_t index = HashGeneric(aPc) & kMask;
134
0
    MOZ_ASSERT(index < kNumEntries);
135
0
    Entry& entry = mEntries[index];
136
0
137
0
    if (!entry.mInUse || entry.mPc != aPc) {
138
0
      mNumCacheMisses++;
139
0
140
0
      // MozDescribeCodeAddress can (on Linux) acquire a lock inside
141
0
      // the shared library loader.  Another thread might call malloc
142
0
      // while holding that lock (when loading a shared library).  So
143
0
      // we have to exit the lock around this call.  For details, see
144
0
      // https://bugzilla.mozilla.org/show_bug.cgi?id=363334#c3
145
0
      MozCodeAddressDetails details;
146
0
      {
147
0
        DescribeCodeAddressLock::Unlock();
148
0
        (void)MozDescribeCodeAddress(const_cast<void*>(aPc), &details);
149
0
        DescribeCodeAddressLock::Lock();
150
0
      }
151
0
152
0
      const char* library = mLibraryStrings.Intern(details.library);
153
0
      entry.Replace(aPc, details.function, library, details.loffset,
154
0
                    details.filename, details.lineno);
155
0
156
0
    } else {
157
0
      mNumCacheHits++;
158
0
    }
159
0
160
0
    MOZ_ASSERT(entry.mPc == aPc);
161
0
162
0
    MozFormatCodeAddress(aBuf, aBufLen, aFrameNumber, entry.mPc,
163
0
                         entry.mFunction, entry.mLibrary, entry.mLOffset,
164
0
                         entry.mFileName, entry.mLineNo);
165
0
  }
166
167
  size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
168
  {
169
    size_t n = aMallocSizeOf(this);
170
    for (uint32_t i = 0; i < kNumEntries; i++) {
171
      n += mEntries[i].SizeOfExcludingThis(aMallocSizeOf);
172
    }
173
174
    n += mLibraryStrings.SizeOfExcludingThis(aMallocSizeOf);
175
176
    return n;
177
  }
178
179
  size_t CacheCapacity() const { return kNumEntries; }
180
181
  size_t CacheCount() const
182
  {
183
    size_t n = 0;
184
    for (size_t i = 0; i < kNumEntries; i++) {
185
      if (mEntries[i].mInUse) {
186
        n++;
187
      }
188
    }
189
    return n;
190
  }
191
192
  size_t NumCacheHits()   const { return mNumCacheHits; }
193
  size_t NumCacheMisses() const { return mNumCacheMisses; }
194
};
195
196
} // namespace mozilla
197
198
#endif // CodeAddressService_h__