Line data Source code
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 0 : 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 0 : Entry()
69 : : mPc(0), mFunction(nullptr), mLibrary(nullptr), mLOffset(0),
70 0 : mFileName(nullptr), mLineNo(0), mInUse(0)
71 0 : {}
72 :
73 0 : ~Entry()
74 : {
75 : // We don't free mLibrary because it's externally owned.
76 0 : StringAlloc::free(mFunction);
77 0 : StringAlloc::free(mFileName);
78 0 : }
79 :
80 0 : void Replace(const void* aPc, const char* aFunction,
81 : const char* aLibrary, ptrdiff_t aLOffset,
82 : const char* aFileName, unsigned long aLineNo)
83 : {
84 0 : mPc = aPc;
85 :
86 : // 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 :
92 0 : mLibrary = aLibrary;
93 0 : mLOffset = aLOffset;
94 0 : mLineNo = aLineNo;
95 :
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 0 : CodeAddressService()
124 0 : : mEntries(), mNumCacheHits(0), mNumCacheMisses(0)
125 : {
126 0 : }
127 :
128 0 : void GetLocation(uint32_t aFrameNumber, const void* aPc, char* aBuf,
129 : size_t aBufLen)
130 : {
131 0 : MOZ_ASSERT(DescribeCodeAddressLock::IsLocked());
132 :
133 0 : uint32_t index = HashGeneric(aPc) & kMask;
134 0 : MOZ_ASSERT(index < kNumEntries);
135 0 : Entry& entry = mEntries[index];
136 :
137 0 : if (!entry.mInUse || entry.mPc != aPc) {
138 0 : mNumCacheMisses++;
139 :
140 : // MozDescribeCodeAddress can (on Linux) acquire a lock inside
141 : // the shared library loader. Another thread might call malloc
142 : // while holding that lock (when loading a shared library). So
143 : // we have to exit the lock around this call. For details, see
144 : // https://bugzilla.mozilla.org/show_bug.cgi?id=363334#c3
145 : MozCodeAddressDetails details;
146 : {
147 0 : DescribeCodeAddressLock::Unlock();
148 0 : (void)MozDescribeCodeAddress(const_cast<void*>(aPc), &details);
149 0 : DescribeCodeAddressLock::Lock();
150 : }
151 :
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 :
156 : } else {
157 0 : mNumCacheHits++;
158 : }
159 :
160 0 : MOZ_ASSERT(entry.mPc == aPc);
161 :
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__
|