tools/profiler/LulMain.h

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

michael@0 1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
michael@0 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
michael@0 3 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 6
michael@0 7 #ifndef LulMain_h
michael@0 8 #define LulMain_h
michael@0 9
michael@0 10 #include <pthread.h> // pthread_t
michael@0 11
michael@0 12 #include <map>
michael@0 13
michael@0 14 #include "LulPlatformMacros.h"
michael@0 15 #include "LulRWLock.h"
michael@0 16
michael@0 17 // LUL: A Lightweight Unwind Library.
michael@0 18 // This file provides the end-user (external) interface for LUL.
michael@0 19
michael@0 20 // Some comments about naming in the implementation. These are safe
michael@0 21 // to ignore if you are merely using LUL, but are important if you
michael@0 22 // hack on its internals.
michael@0 23 //
michael@0 24 // Debuginfo readers in general have tended to use the word "address"
michael@0 25 // to mean several different things. This sometimes makes them
michael@0 26 // difficult to understand and maintain. LUL tries hard to avoid
michael@0 27 // using the word "address" and instead uses the following more
michael@0 28 // precise terms:
michael@0 29 //
michael@0 30 // * SVMA ("Stated Virtual Memory Address"): this is an address of a
michael@0 31 // symbol (etc) as it is stated in the symbol table, or other
michael@0 32 // metadata, of an object. Such values are typically small and
michael@0 33 // start from zero or thereabouts, unless the object has been
michael@0 34 // prelinked.
michael@0 35 //
michael@0 36 // * AVMA ("Actual Virtual Memory Address"): this is the address of a
michael@0 37 // symbol (etc) in a running process, that is, once the associated
michael@0 38 // object has been mapped into a process. Such values are typically
michael@0 39 // much larger than SVMAs, since objects can get mapped arbitrarily
michael@0 40 // far along the address space.
michael@0 41 //
michael@0 42 // * "Bias": the difference between AVMA and SVMA for a given symbol
michael@0 43 // (specifically, AVMA - SVMA). The bias is always an integral
michael@0 44 // number of pages. Once we know the bias for a given object's
michael@0 45 // text section (for example), we can compute the AVMAs of all of
michael@0 46 // its text symbols by adding the bias to their SVMAs.
michael@0 47 //
michael@0 48 // * "Image address": typically, to read debuginfo from an object we
michael@0 49 // will temporarily mmap in the file so as to read symbol tables
michael@0 50 // etc. Addresses in this temporary mapping are called "Image
michael@0 51 // addresses". Note that the temporary mapping is entirely
michael@0 52 // unrelated to the mappings of the file that the dynamic linker
michael@0 53 // must perform merely in order to get the program to run. Hence
michael@0 54 // image addresses are unrelated to either SVMAs or AVMAs.
michael@0 55
michael@0 56
michael@0 57 namespace lul {
michael@0 58
michael@0 59 // A machine word plus validity tag.
michael@0 60 class TaggedUWord {
michael@0 61 public:
michael@0 62 // Construct a valid one.
michael@0 63 TaggedUWord(uintptr_t w)
michael@0 64 : mValue(w)
michael@0 65 , mValid(true)
michael@0 66 {}
michael@0 67
michael@0 68 // Construct an invalid one.
michael@0 69 TaggedUWord()
michael@0 70 : mValue(0)
michael@0 71 , mValid(false)
michael@0 72 {}
michael@0 73
michael@0 74 // Add in a second one.
michael@0 75 void Add(TaggedUWord other) {
michael@0 76 if (mValid && other.Valid()) {
michael@0 77 mValue += other.Value();
michael@0 78 } else {
michael@0 79 mValue = 0;
michael@0 80 mValid = false;
michael@0 81 }
michael@0 82 }
michael@0 83
michael@0 84 // Is it word-aligned?
michael@0 85 bool IsAligned() const {
michael@0 86 return mValid && (mValue & (sizeof(uintptr_t)-1)) == 0;
michael@0 87 }
michael@0 88
michael@0 89 uintptr_t Value() const { return mValue; }
michael@0 90 bool Valid() const { return mValid; }
michael@0 91
michael@0 92 private:
michael@0 93 uintptr_t mValue;
michael@0 94 bool mValid;
michael@0 95 };
michael@0 96
michael@0 97
michael@0 98 // The registers, with validity tags, that will be unwound.
michael@0 99
michael@0 100 struct UnwindRegs {
michael@0 101 #if defined(LUL_ARCH_arm)
michael@0 102 TaggedUWord r7;
michael@0 103 TaggedUWord r11;
michael@0 104 TaggedUWord r12;
michael@0 105 TaggedUWord r13;
michael@0 106 TaggedUWord r14;
michael@0 107 TaggedUWord r15;
michael@0 108 #elif defined(LUL_ARCH_x64) || defined(LUL_ARCH_x86)
michael@0 109 TaggedUWord xbp;
michael@0 110 TaggedUWord xsp;
michael@0 111 TaggedUWord xip;
michael@0 112 #else
michael@0 113 # error "Unknown plat"
michael@0 114 #endif
michael@0 115 };
michael@0 116
michael@0 117
michael@0 118 // The maximum number of bytes in a stack snapshot. This can be
michael@0 119 // increased if necessary, but larger values cost performance, since a
michael@0 120 // stack snapshot needs to be copied between sampling and worker
michael@0 121 // threads for each snapshot. In practice 32k seems to be enough
michael@0 122 // to get good backtraces.
michael@0 123 static const size_t N_STACK_BYTES = 32768;
michael@0 124
michael@0 125 // The stack chunk image that will be unwound.
michael@0 126 struct StackImage {
michael@0 127 // [start_avma, +len) specify the address range in the buffer.
michael@0 128 // Obviously we require 0 <= len <= N_STACK_BYTES.
michael@0 129 uintptr_t mStartAvma;
michael@0 130 size_t mLen;
michael@0 131 uint8_t mContents[N_STACK_BYTES];
michael@0 132 };
michael@0 133
michael@0 134
michael@0 135 // The core unwinder library class. Just one of these is needed, and
michael@0 136 // it can be shared by multiple unwinder threads.
michael@0 137 //
michael@0 138 // Access to the library is mediated by a single reader-writer lock.
michael@0 139 // All attempts to change the library's internal shared state -- that
michael@0 140 // is, loading or unloading unwind info -- are forced single-threaded
michael@0 141 // by causing the called routine to acquire a write-lock. Unwind
michael@0 142 // requests do not change the library's internal shared state and
michael@0 143 // therefore require only a read-lock. Hence multiple threads can
michael@0 144 // unwind in parallel.
michael@0 145 //
michael@0 146 // The library needs to maintain state which is private to each
michael@0 147 // unwinder thread -- the CFI (Dwarf Call Frame Information) fast
michael@0 148 // cache. Hence unwinder threads first need to register with the
michael@0 149 // library, so their identities are known. Also, for maximum
michael@0 150 // effectiveness of the CFI caching, it is preferable to have a small
michael@0 151 // number of very-busy unwinder threads rather than a large number of
michael@0 152 // mostly-idle unwinder threads.
michael@0 153 //
michael@0 154 // None of the methods may be safely called from within a signal
michael@0 155 // handler, since this risks deadlock. In particular this means
michael@0 156 // a thread may not unwind itself from within a signal handler
michael@0 157 // frame. It might be safe to call Unwind() on its own stack
michael@0 158 // from not-inside a signal frame, although even that cannot be
michael@0 159 // guaranteed deadlock free.
michael@0 160
michael@0 161 class PriMap;
michael@0 162 class SegArray;
michael@0 163 class CFICache;
michael@0 164
michael@0 165 class LUL {
michael@0 166 public:
michael@0 167 // Create; supply a logging sink. Initialises the rw-lock.
michael@0 168 LUL(void (*aLog)(const char*));
michael@0 169
michael@0 170 // Destroy. This acquires mRWlock for writing. By doing that, waits
michael@0 171 // for all unwinder threads to finish any Unwind() calls they may be
michael@0 172 // in. All resources are freed and all registered unwinder threads
michael@0 173 // are deregistered.
michael@0 174 ~LUL();
michael@0 175
michael@0 176 // Notify of a new r-x mapping, and load the associated unwind info.
michael@0 177 // The filename is strdup'd and used for debug printing. If
michael@0 178 // aMappedImage is NULL, this function will mmap/munmap the file
michael@0 179 // itself, so as to be able to read the unwind info. If
michael@0 180 // aMappedImage is non-NULL then it is assumed to point to a
michael@0 181 // called-supplied and caller-managed mapped image of the file.
michael@0 182 //
michael@0 183 // Acquires mRWlock for writing. This must be called only after the
michael@0 184 // code area in question really has been mapped.
michael@0 185 void NotifyAfterMap(uintptr_t aRXavma, size_t aSize,
michael@0 186 const char* aFileName, const void* aMappedImage);
michael@0 187
michael@0 188 // In rare cases we know an executable area exists but don't know
michael@0 189 // what the associated file is. This call notifies LUL of such
michael@0 190 // areas. This is important for correct functioning of stack
michael@0 191 // scanning and of the x86-{linux,android} special-case
michael@0 192 // __kernel_syscall function handling. Acquires mRWlock for
michael@0 193 // writing. This must be called only after the code area in
michael@0 194 // question really has been mapped.
michael@0 195 void NotifyExecutableArea(uintptr_t aRXavma, size_t aSize);
michael@0 196
michael@0 197 // Notify that a mapped area has been unmapped; discard any
michael@0 198 // associated unwind info. Acquires mRWlock for writing. Note that
michael@0 199 // to avoid segfaulting the stack-scan unwinder, which inspects code
michael@0 200 // areas, this must be called before the code area in question is
michael@0 201 // really unmapped. Note that, unlike NotifyAfterMap(), this
michael@0 202 // function takes the start and end addresses of the range to be
michael@0 203 // unmapped, rather than a start and a length parameter. This is so
michael@0 204 // as to make it possible to notify an unmap for the entire address
michael@0 205 // space using a single call.
michael@0 206 void NotifyBeforeUnmap(uintptr_t aAvmaMin, uintptr_t aAvmaMax);
michael@0 207
michael@0 208 // Apply NotifyBeforeUnmap to the entire address space. This causes
michael@0 209 // LUL to discard all unwind and executable-area information for the
michael@0 210 // entire address space.
michael@0 211 void NotifyBeforeUnmapAll() {
michael@0 212 NotifyBeforeUnmap(0, UINTPTR_MAX);
michael@0 213 }
michael@0 214
michael@0 215 // Returns the number of mappings currently registered. Acquires
michael@0 216 // mRWlock for writing.
michael@0 217 size_t CountMappings();
michael@0 218
michael@0 219 // Register the calling thread for unwinding. Acquires mRWlock for
michael@0 220 // writing.
michael@0 221 void RegisterUnwinderThread();
michael@0 222
michael@0 223 // Unwind |aStackImg| starting with the context in |aStartRegs|.
michael@0 224 // Write the number of frames recovered in *aFramesUsed. Put
michael@0 225 // the PC values in aFramePCs[0 .. *aFramesUsed-1] and
michael@0 226 // the SP values in aFrameSPs[0 .. *aFramesUsed-1].
michael@0 227 // |aFramesAvail| is the size of the two output arrays and hence the
michael@0 228 // largest possible value of *aFramesUsed. PC values are always
michael@0 229 // valid, and the unwind will stop when the PC becomes invalid, but
michael@0 230 // the SP values might be invalid, in which case the value zero will
michael@0 231 // be written in the relevant frameSPs[] slot.
michael@0 232 //
michael@0 233 // Unwinding may optionally use stack scanning. The maximum number
michael@0 234 // of frames that may be recovered by stack scanning is
michael@0 235 // |aScannedFramesAllowed| and the actual number recovered is
michael@0 236 // written into *aScannedFramesAcquired. |aScannedFramesAllowed|
michael@0 237 // must be less than or equal to |aFramesAvail|.
michael@0 238 //
michael@0 239 // This function assumes that the SP values increase as it unwinds
michael@0 240 // away from the innermost frame -- that is, that the stack grows
michael@0 241 // down. It monitors SP values as it unwinds to check they
michael@0 242 // decrease, so as to avoid looping on corrupted stacks.
michael@0 243 //
michael@0 244 // Acquires mRWlock for reading. Hence multiple threads may unwind
michael@0 245 // at once, but no thread may be unwinding whilst the library loads
michael@0 246 // or discards unwind information. Returns false if the calling
michael@0 247 // thread is not registered for unwinding.
michael@0 248 //
michael@0 249 // Up to aScannedFramesAllowed stack-scanned frames may be recovered.
michael@0 250 //
michael@0 251 // The calling thread must previously have registered itself via
michael@0 252 // RegisterUnwinderThread.
michael@0 253 void Unwind(/*OUT*/uintptr_t* aFramePCs,
michael@0 254 /*OUT*/uintptr_t* aFrameSPs,
michael@0 255 /*OUT*/size_t* aFramesUsed,
michael@0 256 /*OUT*/size_t* aScannedFramesAcquired,
michael@0 257 size_t aFramesAvail,
michael@0 258 size_t aScannedFramesAllowed,
michael@0 259 UnwindRegs* aStartRegs, StackImage* aStackImg);
michael@0 260
michael@0 261 // The logging sink. Call to send debug strings to the caller-
michael@0 262 // specified destination.
michael@0 263 void (*mLog)(const char*);
michael@0 264
michael@0 265 private:
michael@0 266 // Invalidate the caches. Requires mRWlock to be held for writing;
michael@0 267 // does not acquire it itself.
michael@0 268 void InvalidateCFICaches();
michael@0 269
michael@0 270 // The one-and-only lock, a reader-writer lock, for the library.
michael@0 271 LulRWLock* mRWlock;
michael@0 272
michael@0 273 // The top level mapping from code address ranges to postprocessed
michael@0 274 // unwind info. Basically a sorted array of (addr, len, info)
michael@0 275 // records. Threads wishing to query this field must hold mRWlock
michael@0 276 // for reading. Threads wishing to modify this field must hold
michael@0 277 // mRWlock for writing. This field is updated by NotifyAfterMap and
michael@0 278 // NotifyBeforeUnmap.
michael@0 279 PriMap* mPriMap;
michael@0 280
michael@0 281 // An auxiliary structure that records which address ranges are
michael@0 282 // mapped r-x, for the benefit of the stack scanner. Threads
michael@0 283 // wishing to query this field must hold mRWlock for reading.
michael@0 284 // Threads wishing to modify this field must hold mRWlock for
michael@0 285 // writing.
michael@0 286 SegArray* mSegArray;
michael@0 287
michael@0 288 // The thread-local data: a mapping from threads to CFI-fast-caches.
michael@0 289 // Threads wishing to query this field must hold mRWlock for
michael@0 290 // reading. Threads wishing to modify this field must hold mRWlock
michael@0 291 // for writing.
michael@0 292 //
michael@0 293 // The CFICaches themselves are thread-local and can be both read
michael@0 294 // and written when mRWlock is held for reading. It would probably
michael@0 295 // be faster to use the pthread_{set,get}specific functions, but
michael@0 296 // also more difficult. This map is queried once per unwind, in
michael@0 297 // order to get hold of the CFI cache for a given thread.
michael@0 298 std::map<pthread_t, CFICache*> mCaches;
michael@0 299 };
michael@0 300
michael@0 301
michael@0 302 // Run unit tests on an initialised, loaded-up LUL instance, and print
michael@0 303 // summary results on |aLUL|'s logging sink. Also return the number
michael@0 304 // of tests run in *aNTests and the number that passed in
michael@0 305 // *aNTestsPassed.
michael@0 306 void
michael@0 307 RunLulUnitTests(/*OUT*/int* aNTests, /*OUT*/int*aNTestsPassed, LUL* aLUL);
michael@0 308
michael@0 309 } // namespace lul
michael@0 310
michael@0 311 #endif // LulMain_h

mercurial