Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/tools/profiler/lul/LulMain.cpp
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
#include "LulMain.h"
8
9
#include <string.h>
10
#include <stdlib.h>
11
#include <stdio.h>
12
#include <unistd.h>   // write(), only for testing LUL
13
14
#include <algorithm>  // std::sort
15
#include <string>
16
17
#include "mozilla/Assertions.h"
18
#include "mozilla/ArrayUtils.h"
19
#include "mozilla/CheckedInt.h"
20
#include "mozilla/DebugOnly.h"
21
#include "mozilla/MemoryChecking.h"
22
#include "mozilla/Move.h"
23
#include "mozilla/Sprintf.h"
24
#include "mozilla/UniquePtr.h"
25
#include "mozilla/Unused.h"
26
27
#include "LulCommonExt.h"
28
#include "LulElfExt.h"
29
30
#include "LulMainInt.h"
31
32
#include "platform-linux-lul.h"  // for gettid()
33
34
// Set this to 1 for verbose logging
35
0
#define DEBUG_MAIN 0
36
37
namespace lul {
38
39
using std::string;
40
using std::vector;
41
using std::pair;
42
using mozilla::CheckedInt;
43
using mozilla::DebugOnly;
44
using mozilla::MallocSizeOf;
45
using mozilla::Unused;
46
47
48
// WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING
49
//
50
// Some functions in this file are marked RUNS IN NO-MALLOC CONTEXT.
51
// Any such function -- and, hence, the transitive closure of those
52
// reachable from it -- must not do any dynamic memory allocation.
53
// Doing so risks deadlock.  There is exactly one root function for
54
// the transitive closure: Lul::Unwind.
55
//
56
// WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING
57
58
59
////////////////////////////////////////////////////////////////
60
// RuleSet                                                    //
61
////////////////////////////////////////////////////////////////
62
63
static const char*
64
NameOf_DW_REG(int16_t aReg)
65
0
{
66
0
  switch (aReg) {
67
0
    case DW_REG_CFA:       return "cfa";
68
0
#if defined(GP_ARCH_amd64) || defined(GP_ARCH_x86)
69
0
    case DW_REG_INTEL_XBP: return "xbp";
70
0
    case DW_REG_INTEL_XSP: return "xsp";
71
0
    case DW_REG_INTEL_XIP: return "xip";
72
#elif defined(GP_ARCH_arm)
73
    case DW_REG_ARM_R7:    return "r7";
74
    case DW_REG_ARM_R11:   return "r11";
75
    case DW_REG_ARM_R12:   return "r12";
76
    case DW_REG_ARM_R13:   return "r13";
77
    case DW_REG_ARM_R14:   return "r14";
78
    case DW_REG_ARM_R15:   return "r15";
79
#elif defined(GP_ARCH_arm64)
80
    case DW_REG_AARCH64_X29: return "x29";
81
    case DW_REG_AARCH64_X30: return "x30";
82
    case DW_REG_AARCH64_SP:  return "sp";
83
#elif defined(GP_ARCH_mips64)
84
    case DW_REG_MIPS_SP:   return "sp";
85
    case DW_REG_MIPS_FP:   return "fp";
86
    case DW_REG_MIPS_PC:   return "pc";
87
#else
88
# error "Unsupported arch"
89
#endif
90
0
    default: return "???";
91
0
  }
92
0
}
93
94
string
95
LExpr::ShowRule(const char* aNewReg) const
96
0
{
97
0
  char buf[64];
98
0
  string res = string(aNewReg) + "=";
99
0
  switch (mHow) {
100
0
    case UNKNOWN:
101
0
      res += "Unknown";
102
0
      break;
103
0
    case NODEREF:
104
0
      SprintfLiteral(buf, "%s+%d",
105
0
                     NameOf_DW_REG(mReg), (int)mOffset);
106
0
      res += buf;
107
0
      break;
108
0
    case DEREF:
109
0
      SprintfLiteral(buf, "*(%s+%d)",
110
0
                     NameOf_DW_REG(mReg), (int)mOffset);
111
0
      res += buf;
112
0
      break;
113
0
    case PFXEXPR:
114
0
      SprintfLiteral(buf, "PfxExpr-at-%d", (int)mOffset);
115
0
      res += buf;
116
0
      break;
117
0
    default:
118
0
      res += "???";
119
0
      break;
120
0
  }
121
0
  return res;
122
0
}
123
124
void
125
RuleSet::Print(void(*aLog)(const char*)) const
126
0
{
127
0
  char buf[96];
128
0
  SprintfLiteral(buf, "[%llx .. %llx]: let ",
129
0
                 (unsigned long long int)mAddr,
130
0
                 (unsigned long long int)(mAddr + mLen - 1));
131
0
  string res = string(buf);
132
0
  res += mCfaExpr.ShowRule("cfa");
133
0
  res += " in";
134
0
  // For each reg we care about, print the recovery expression.
135
0
#if defined(GP_ARCH_amd64) || defined(GP_ARCH_x86)
136
0
  res += mXipExpr.ShowRule(" RA");
137
0
  res += mXspExpr.ShowRule(" SP");
138
0
  res += mXbpExpr.ShowRule(" BP");
139
#elif defined(GP_ARCH_arm)
140
  res += mR15expr.ShowRule(" R15");
141
  res += mR7expr .ShowRule(" R7" );
142
  res += mR11expr.ShowRule(" R11");
143
  res += mR12expr.ShowRule(" R12");
144
  res += mR13expr.ShowRule(" R13");
145
  res += mR14expr.ShowRule(" R14");
146
#elif defined(GP_ARCH_arm64)
147
  res += mX29expr.ShowRule(" X29");
148
  res += mX30expr.ShowRule(" X30");
149
  res += mSPexpr .ShowRule(" SP");
150
#elif defined(GP_ARCH_mips64)
151
  res += mPCexpr.ShowRule(" PC");
152
  res += mSPexpr.ShowRule(" SP");
153
  res += mFPexpr.ShowRule(" FP");
154
#else
155
# error "Unsupported arch"
156
#endif
157
  aLog(res.c_str());
158
0
}
159
160
LExpr*
161
0
RuleSet::ExprForRegno(DW_REG_NUMBER aRegno) {
162
0
  switch (aRegno) {
163
0
    case DW_REG_CFA: return &mCfaExpr;
164
0
#   if defined(GP_ARCH_amd64) || defined(GP_ARCH_x86)
165
0
    case DW_REG_INTEL_XIP: return &mXipExpr;
166
0
    case DW_REG_INTEL_XSP: return &mXspExpr;
167
0
    case DW_REG_INTEL_XBP: return &mXbpExpr;
168
#   elif defined(GP_ARCH_arm)
169
    case DW_REG_ARM_R15:   return &mR15expr;
170
    case DW_REG_ARM_R14:   return &mR14expr;
171
    case DW_REG_ARM_R13:   return &mR13expr;
172
    case DW_REG_ARM_R12:   return &mR12expr;
173
    case DW_REG_ARM_R11:   return &mR11expr;
174
    case DW_REG_ARM_R7:    return &mR7expr;
175
#   elif defined(GP_ARCH_arm64)
176
    case DW_REG_AARCH64_X29: return &mX29expr;
177
    case DW_REG_AARCH64_X30: return &mX30expr;
178
    case DW_REG_AARCH64_SP:  return &mSPexpr;
179
#elif defined(GP_ARCH_mips64)
180
    case DW_REG_MIPS_SP:    return &mSPexpr;
181
    case DW_REG_MIPS_FP:    return &mFPexpr;
182
    case DW_REG_MIPS_PC:    return &mPCexpr;
183
#   else
184
#     error "Unknown arch"
185
#   endif
186
0
    default: return nullptr;
187
0
  }
188
0
}
189
190
RuleSet::RuleSet()
191
0
{
192
0
  mAddr = 0;
193
0
  mLen  = 0;
194
0
  // The only other fields are of type LExpr and those are initialised
195
0
  // by LExpr::LExpr().
196
0
}
197
198
199
////////////////////////////////////////////////////////////////
200
// SecMap                                                     //
201
////////////////////////////////////////////////////////////////
202
203
// See header file LulMainInt.h for comments about invariants.
204
205
SecMap::SecMap(void(*aLog)(const char*))
206
  : mSummaryMinAddr(1)
207
  , mSummaryMaxAddr(0)
208
  , mUsable(true)
209
  , mLog(aLog)
210
0
{}
211
212
0
SecMap::~SecMap() {
213
0
  mRuleSets.clear();
214
0
}
215
216
// RUNS IN NO-MALLOC CONTEXT
217
RuleSet*
218
0
SecMap::FindRuleSet(uintptr_t ia) {
219
0
  // Binary search mRuleSets to find one that brackets |ia|.
220
0
  // lo and hi need to be signed, else the loop termination tests
221
0
  // don't work properly.  Note that this works correctly even when
222
0
  // mRuleSets.size() == 0.
223
0
224
0
  // Can't do this until the array has been sorted and preened.
225
0
  MOZ_ASSERT(mUsable);
226
0
227
0
  long int lo = 0;
228
0
  long int hi = (long int)mRuleSets.size() - 1;
229
0
  while (true) {
230
0
    // current unsearched space is from lo to hi, inclusive.
231
0
    if (lo > hi) {
232
0
      // not found
233
0
      return nullptr;
234
0
    }
235
0
    long int  mid         = lo + ((hi - lo) / 2);
236
0
    RuleSet*  mid_ruleSet = &mRuleSets[mid];
237
0
    uintptr_t mid_minAddr = mid_ruleSet->mAddr;
238
0
    uintptr_t mid_maxAddr = mid_minAddr + mid_ruleSet->mLen - 1;
239
0
    if (ia < mid_minAddr) { hi = mid-1; continue; }
240
0
    if (ia > mid_maxAddr) { lo = mid+1; continue; }
241
0
    MOZ_ASSERT(mid_minAddr <= ia && ia <= mid_maxAddr);
242
0
    return mid_ruleSet;
243
0
  }
244
0
  // NOTREACHED
245
0
}
246
247
// Add a RuleSet to the collection.  The rule is copied in.  Calling
248
// this makes the map non-searchable.
249
void
250
0
SecMap::AddRuleSet(const RuleSet* rs) {
251
0
  mUsable = false;
252
0
  mRuleSets.push_back(*rs);
253
0
}
254
255
// Add a PfxInstr to the vector of such instrs, and return the index
256
// in the vector.  Calling this makes the map non-searchable.
257
uint32_t
258
0
SecMap::AddPfxInstr(PfxInstr pfxi) {
259
0
  mUsable = false;
260
0
  mPfxInstrs.push_back(pfxi);
261
0
  return mPfxInstrs.size() - 1;
262
0
}
263
264
265
static bool
266
0
CmpRuleSetsByAddrLE(const RuleSet& rs1, const RuleSet& rs2) {
267
0
  return rs1.mAddr < rs2.mAddr;
268
0
}
269
270
// Prepare the map for searching.  Completely remove any which don't
271
// fall inside the specified range [start, +len).
272
void
273
SecMap::PrepareRuleSets(uintptr_t aStart, size_t aLen)
274
0
{
275
0
  if (mRuleSets.empty()) {
276
0
    return;
277
0
  }
278
0
279
0
  MOZ_ASSERT(aLen > 0);
280
0
  if (aLen == 0) {
281
0
    // This should never happen.
282
0
    mRuleSets.clear();
283
0
    return;
284
0
  }
285
0
286
0
  // Sort by start addresses.
287
0
  std::sort(mRuleSets.begin(), mRuleSets.end(), CmpRuleSetsByAddrLE);
288
0
289
0
  // Detect any entry not completely contained within [start, +len).
290
0
  // Set its length to zero, so that the next pass will remove it.
291
0
  for (size_t i = 0; i < mRuleSets.size(); ++i) {
292
0
    RuleSet* rs = &mRuleSets[i];
293
0
    if (rs->mLen > 0 &&
294
0
        (rs->mAddr < aStart || rs->mAddr + rs->mLen > aStart + aLen)) {
295
0
      rs->mLen = 0;
296
0
    }
297
0
  }
298
0
299
0
  // Iteratively truncate any overlaps and remove any zero length
300
0
  // entries that might result, or that may have been present
301
0
  // initially.  Unless the input is seriously screwy, this is
302
0
  // expected to iterate only once.
303
0
  while (true) {
304
0
    size_t i;
305
0
    size_t n = mRuleSets.size();
306
0
    size_t nZeroLen = 0;
307
0
308
0
    if (n == 0) {
309
0
      break;
310
0
    }
311
0
312
0
    for (i = 1; i < n; ++i) {
313
0
      RuleSet* prev = &mRuleSets[i-1];
314
0
      RuleSet* here = &mRuleSets[i];
315
0
      MOZ_ASSERT(prev->mAddr <= here->mAddr);
316
0
      if (prev->mAddr + prev->mLen > here->mAddr) {
317
0
        prev->mLen = here->mAddr - prev->mAddr;
318
0
      }
319
0
      if (prev->mLen == 0)
320
0
        nZeroLen++;
321
0
    }
322
0
323
0
    if (mRuleSets[n-1].mLen == 0) {
324
0
      nZeroLen++;
325
0
    }
326
0
327
0
    // At this point, the entries are in-order and non-overlapping.
328
0
    // If none of them are zero-length, we are done.
329
0
    if (nZeroLen == 0) {
330
0
      break;
331
0
    }
332
0
333
0
    // Slide back the entries to remove the zero length ones.
334
0
    size_t j = 0;  // The write-point.
335
0
    for (i = 0; i < n; ++i) {
336
0
      if (mRuleSets[i].mLen == 0) {
337
0
        continue;
338
0
      }
339
0
      if (j != i) mRuleSets[j] = mRuleSets[i];
340
0
      ++j;
341
0
    }
342
0
    MOZ_ASSERT(i == n);
343
0
    MOZ_ASSERT(nZeroLen <= n);
344
0
    MOZ_ASSERT(j == n - nZeroLen);
345
0
    while (nZeroLen > 0) {
346
0
      mRuleSets.pop_back();
347
0
      nZeroLen--;
348
0
    }
349
0
350
0
    MOZ_ASSERT(mRuleSets.size() == j);
351
0
  }
352
0
353
0
  size_t n = mRuleSets.size();
354
0
355
#ifdef DEBUG
356
  // Do a final check on the rules: their address ranges must be
357
  // ascending, non overlapping, non zero sized.
358
  if (n > 0) {
359
    MOZ_ASSERT(mRuleSets[0].mLen > 0);
360
    for (size_t i = 1; i < n; ++i) {
361
      RuleSet* prev = &mRuleSets[i-1];
362
      RuleSet* here = &mRuleSets[i];
363
      MOZ_ASSERT(prev->mAddr < here->mAddr);
364
      MOZ_ASSERT(here->mLen > 0);
365
      MOZ_ASSERT(prev->mAddr + prev->mLen <= here->mAddr);
366
    }
367
  }
368
#endif
369
370
0
  // Set the summary min and max address values.
371
0
  if (n == 0) {
372
0
    // Use the values defined in comments in the class declaration.
373
0
    mSummaryMinAddr = 1;
374
0
    mSummaryMaxAddr = 0;
375
0
  } else {
376
0
    mSummaryMinAddr = mRuleSets[0].mAddr;
377
0
    mSummaryMaxAddr = mRuleSets[n-1].mAddr + mRuleSets[n-1].mLen - 1;
378
0
  }
379
0
  char buf[150];
380
0
  SprintfLiteral(buf,
381
0
                 "PrepareRuleSets: %d entries, smin/smax 0x%llx, 0x%llx\n",
382
0
                 (int)n, (unsigned long long int)mSummaryMinAddr,
383
0
                         (unsigned long long int)mSummaryMaxAddr);
384
0
  buf[sizeof(buf)-1] = 0;
385
0
  mLog(buf);
386
0
387
0
  // Is now usable for binary search.
388
0
  mUsable = true;
389
0
390
0
  if (0) {
391
0
    mLog("\nRulesets after preening\n");
392
0
    for (size_t i = 0; i < mRuleSets.size(); ++i) {
393
0
      mRuleSets[i].Print(mLog);
394
0
      mLog("\n");
395
0
    }
396
0
    mLog("\n");
397
0
  }
398
0
}
399
400
0
bool SecMap::IsEmpty() {
401
0
  return mRuleSets.empty();
402
0
}
403
404
size_t
405
SecMap::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
406
0
{
407
0
  size_t n = aMallocSizeOf(this);
408
0
409
0
  // It's conceivable that these calls would be unsafe with some
410
0
  // implementations of std::vector, but it seems to be working for now...
411
0
  n += aMallocSizeOf(mRuleSets.data());
412
0
  n += aMallocSizeOf(mPfxInstrs.data());
413
0
414
0
  return n;
415
0
}
416
417
////////////////////////////////////////////////////////////////
418
// SegArray                                                   //
419
////////////////////////////////////////////////////////////////
420
421
// A SegArray holds a set of address ranges that together exactly
422
// cover an address range, with no overlaps or holes.  Each range has
423
// an associated value, which in this case has been specialised to be
424
// a simple boolean.  The representation is kept to minimal canonical
425
// form in which adjacent ranges with the same associated value are
426
// merged together.  Each range is represented by a |struct Seg|.
427
//
428
// SegArrays are used to keep track of which parts of the address
429
// space are known to contain instructions.
430
class SegArray {
431
432
 public:
433
0
  void add(uintptr_t lo, uintptr_t hi, bool val) {
434
0
    if (lo > hi) {
435
0
      return;
436
0
    }
437
0
    split_at(lo);
438
0
    if (hi < UINTPTR_MAX) {
439
0
      split_at(hi+1);
440
0
    }
441
0
    std::vector<Seg>::size_type iLo, iHi, i;
442
0
    iLo = find(lo);
443
0
    iHi = find(hi);
444
0
    for (i = iLo; i <= iHi; ++i) {
445
0
      mSegs[i].val = val;
446
0
    }
447
0
    preen();
448
0
  }
449
450
  // RUNS IN NO-MALLOC CONTEXT
451
  bool getBoundingCodeSegment(/*OUT*/uintptr_t* rx_min,
452
0
                              /*OUT*/uintptr_t* rx_max, uintptr_t addr) {
453
0
    std::vector<Seg>::size_type i = find(addr);
454
0
    if (!mSegs[i].val) {
455
0
      return false;
456
0
    }
457
0
    *rx_min = mSegs[i].lo;
458
0
    *rx_max = mSegs[i].hi;
459
0
    return true;
460
0
  }
461
462
0
  SegArray() {
463
0
    Seg s(0, UINTPTR_MAX, false);
464
0
    mSegs.push_back(s);
465
0
  }
466
467
 private:
468
  struct Seg {
469
0
    Seg(uintptr_t lo, uintptr_t hi, bool val) : lo(lo), hi(hi), val(val) {}
470
    uintptr_t lo;
471
    uintptr_t hi;
472
    bool val;
473
  };
474
475
0
  void preen() {
476
0
    for (std::vector<Seg>::iterator iter = mSegs.begin();
477
0
         iter < mSegs.end()-1;
478
0
         ++iter) {
479
0
      if (iter[0].val != iter[1].val) {
480
0
        continue;
481
0
      }
482
0
      iter[0].hi = iter[1].hi;
483
0
      mSegs.erase(iter+1);
484
0
      // Back up one, so as not to miss an opportunity to merge
485
0
      // with the entry after this one.
486
0
      --iter;
487
0
    }
488
0
  }
489
490
  // RUNS IN NO-MALLOC CONTEXT
491
0
  std::vector<Seg>::size_type find(uintptr_t a) {
492
0
    long int lo = 0;
493
0
    long int hi = (long int)mSegs.size();
494
0
    while (true) {
495
0
      // The unsearched space is lo .. hi inclusive.
496
0
      if (lo > hi) {
497
0
        // Not found.  This can't happen.
498
0
        return (std::vector<Seg>::size_type)(-1);
499
0
      }
500
0
      long int  mid    = lo + ((hi - lo) / 2);
501
0
      uintptr_t mid_lo = mSegs[mid].lo;
502
0
      uintptr_t mid_hi = mSegs[mid].hi;
503
0
      if (a < mid_lo) { hi = mid-1; continue; }
504
0
      if (a > mid_hi) { lo = mid+1; continue; }
505
0
      return (std::vector<Seg>::size_type)mid;
506
0
    }
507
0
  }
508
509
0
  void split_at(uintptr_t a) {
510
0
    std::vector<Seg>::size_type i = find(a);
511
0
    if (mSegs[i].lo == a) {
512
0
      return;
513
0
    }
514
0
    mSegs.insert( mSegs.begin()+i+1, mSegs[i] );
515
0
    mSegs[i].hi = a-1;
516
0
    mSegs[i+1].lo = a;
517
0
  }
518
519
0
  void show() {
520
0
    printf("<< %d entries:\n", (int)mSegs.size());
521
0
    for (std::vector<Seg>::iterator iter = mSegs.begin();
522
0
         iter < mSegs.end();
523
0
         ++iter) {
524
0
      printf("  %016llx  %016llx  %s\n",
525
0
             (unsigned long long int)(*iter).lo,
526
0
             (unsigned long long int)(*iter).hi,
527
0
             (*iter).val ? "true" : "false");
528
0
    }
529
0
    printf(">>\n");
530
0
  }
531
532
  std::vector<Seg> mSegs;
533
};
534
535
536
////////////////////////////////////////////////////////////////
537
// PriMap                                                     //
538
////////////////////////////////////////////////////////////////
539
540
class PriMap {
541
 public:
542
  explicit PriMap(void (*aLog)(const char*))
543
    : mLog(aLog)
544
0
  {}
545
546
  // RUNS IN NO-MALLOC CONTEXT
547
  pair<const RuleSet*, const vector<PfxInstr>*>
548
  Lookup(uintptr_t ia)
549
0
  {
550
0
    SecMap* sm = FindSecMap(ia);
551
0
    return pair<const RuleSet*, const vector<PfxInstr>*>
552
0
             (sm ? sm->FindRuleSet(ia) : nullptr,
553
0
              sm ? sm->GetPfxInstrs() : nullptr);
554
0
  }
555
556
  // Add a secondary map.  No overlaps allowed w.r.t. existing
557
  // secondary maps.
558
0
  void AddSecMap(mozilla::UniquePtr<SecMap>&& aSecMap) {
559
0
    // We can't add an empty SecMap to the PriMap.  But that's OK
560
0
    // since we'd never be able to find anything in it anyway.
561
0
    if (aSecMap->IsEmpty()) {
562
0
      return;
563
0
    }
564
0
565
0
    // Iterate through the SecMaps and find the right place for this
566
0
    // one.  At the same time, ensure that the in-order
567
0
    // non-overlapping invariant is preserved (and, generally, holds).
568
0
    // FIXME: this gives a cost that is O(N^2) in the total number of
569
0
    // shared objects in the system.  ToDo: better.
570
0
    MOZ_ASSERT(aSecMap->mSummaryMinAddr <= aSecMap->mSummaryMaxAddr);
571
0
572
0
    size_t num_secMaps = mSecMaps.size();
573
0
    uintptr_t i;
574
0
    for (i = 0; i < num_secMaps; ++i) {
575
0
      mozilla::UniquePtr<SecMap>& sm_i = mSecMaps[i];
576
0
      MOZ_ASSERT(sm_i->mSummaryMinAddr <= sm_i->mSummaryMaxAddr);
577
0
      if (aSecMap->mSummaryMinAddr < sm_i->mSummaryMaxAddr) {
578
0
        // |aSecMap| needs to be inserted immediately before mSecMaps[i].
579
0
        break;
580
0
      }
581
0
    }
582
0
    MOZ_ASSERT(i <= num_secMaps);
583
0
    if (i == num_secMaps) {
584
0
      // It goes at the end.
585
0
      mSecMaps.push_back(std::move(aSecMap));
586
0
    } else {
587
0
      std::vector<mozilla::UniquePtr<SecMap>>::iterator iter = mSecMaps.begin() + i;
588
0
      mSecMaps.insert(iter, std::move(aSecMap));
589
0
    }
590
0
    char buf[100];
591
0
    SprintfLiteral(buf, "AddSecMap: now have %d SecMaps\n",
592
0
                   (int)mSecMaps.size());
593
0
    buf[sizeof(buf)-1] = 0;
594
0
    mLog(buf);
595
0
  }
596
597
  // Remove and delete any SecMaps in the mapping, that intersect
598
  // with the specified address range.
599
0
  void RemoveSecMapsInRange(uintptr_t avma_min, uintptr_t avma_max) {
600
0
    MOZ_ASSERT(avma_min <= avma_max);
601
0
    size_t num_secMaps = mSecMaps.size();
602
0
    if (num_secMaps > 0) {
603
0
      intptr_t i;
604
0
      // Iterate from end to start over the vector, so as to ensure
605
0
      // that the special case where |avma_min| and |avma_max| denote
606
0
      // the entire address space, can be completed in time proportional
607
0
      // to the number of elements in the map.
608
0
      for (i = (intptr_t)num_secMaps-1; i >= 0; i--) {
609
0
        mozilla::UniquePtr<SecMap>& sm_i = mSecMaps[i];
610
0
        if (sm_i->mSummaryMaxAddr < avma_min ||
611
0
            avma_max < sm_i->mSummaryMinAddr) {
612
0
          // There's no overlap.  Move on.
613
0
          continue;
614
0
        }
615
0
        // We need to remove mSecMaps[i] and slide all those above it
616
0
        // downwards to cover the hole.
617
0
        mSecMaps.erase(mSecMaps.begin() + i);
618
0
      }
619
0
    }
620
0
  }
621
622
  // Return the number of currently contained SecMaps.
623
0
  size_t CountSecMaps() {
624
0
    return mSecMaps.size();
625
0
  }
626
627
0
  size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const {
628
0
    size_t n = aMallocSizeOf(this);
629
0
630
0
    // It's conceivable that this call would be unsafe with some
631
0
    // implementations of std::vector, but it seems to be working for now...
632
0
    n += aMallocSizeOf(mSecMaps.data());
633
0
634
0
    for (size_t i = 0; i < mSecMaps.size(); i++) {
635
0
      n += mSecMaps[i]->SizeOfIncludingThis(aMallocSizeOf);
636
0
    }
637
0
638
0
    return n;
639
0
  }
640
641
 private:
642
  // RUNS IN NO-MALLOC CONTEXT
643
0
  SecMap* FindSecMap(uintptr_t ia) {
644
0
    // Binary search mSecMaps to find one that brackets |ia|.
645
0
    // lo and hi need to be signed, else the loop termination tests
646
0
    // don't work properly.
647
0
    long int lo = 0;
648
0
    long int hi = (long int)mSecMaps.size() - 1;
649
0
    while (true) {
650
0
      // current unsearched space is from lo to hi, inclusive.
651
0
      if (lo > hi) {
652
0
        // not found
653
0
        return nullptr;
654
0
      }
655
0
      long int  mid         = lo + ((hi - lo) / 2);
656
0
      mozilla::UniquePtr<SecMap>& mid_secMap = mSecMaps[mid];
657
0
      uintptr_t mid_minAddr = mid_secMap->mSummaryMinAddr;
658
0
      uintptr_t mid_maxAddr = mid_secMap->mSummaryMaxAddr;
659
0
      if (ia < mid_minAddr) { hi = mid-1; continue; }
660
0
      if (ia > mid_maxAddr) { lo = mid+1; continue; }
661
0
      MOZ_ASSERT(mid_minAddr <= ia && ia <= mid_maxAddr);
662
0
      return mid_secMap.get();
663
0
    }
664
0
    // NOTREACHED
665
0
  }
666
667
 private:
668
  // sorted array of per-object ranges, non overlapping, non empty
669
  std::vector<mozilla::UniquePtr<SecMap>> mSecMaps;
670
671
  // a logging sink, for debugging.
672
  void (*mLog)(const char*);
673
};
674
675
676
////////////////////////////////////////////////////////////////
677
// LUL                                                        //
678
////////////////////////////////////////////////////////////////
679
680
#define LUL_LOG(_str) \
681
0
  do { \
682
0
    char buf[200]; \
683
0
    SprintfLiteral(buf, \
684
0
                   "LUL: pid %d tid %d lul-obj %p: %s", \
685
0
                   getpid(), gettid(), this, (_str));   \
686
0
    buf[sizeof(buf)-1] = 0; \
687
0
    mLog(buf); \
688
0
  } while (0)
689
690
LUL::LUL(void (*aLog)(const char*))
691
  : mLog(aLog)
692
  , mAdminMode(true)
693
  , mAdminThreadId(gettid())
694
  , mPriMap(new PriMap(aLog))
695
  , mSegArray(new SegArray())
696
  , mUSU(new UniqueStringUniverse())
697
0
{
698
0
  LUL_LOG("LUL::LUL: Created object");
699
0
}
700
701
702
LUL::~LUL()
703
0
{
704
0
  LUL_LOG("LUL::~LUL: Destroyed object");
705
0
  delete mPriMap;
706
0
  delete mSegArray;
707
0
  mLog = nullptr;
708
0
  delete mUSU;
709
0
}
710
711
712
void
713
LUL::MaybeShowStats()
714
0
{
715
0
  // This is racey in the sense that it can't guarantee that
716
0
  //   n_new == n_new_Context + n_new_CFI + n_new_Scanned
717
0
  // if it should happen that mStats is updated by some other thread
718
0
  // in between computation of n_new and n_new_{Context,CFI,FP}.
719
0
  // But it's just stats printing, so we don't really care.
720
0
  uint32_t n_new = mStats - mStatsPrevious;
721
0
  if (n_new >= 5000) {
722
0
    uint32_t n_new_Context = mStats.mContext - mStatsPrevious.mContext;
723
0
    uint32_t n_new_CFI     = mStats.mCFI     - mStatsPrevious.mCFI;
724
0
    uint32_t n_new_FP      = mStats.mFP      - mStatsPrevious.mFP;
725
0
    mStatsPrevious = mStats;
726
0
    char buf[200];
727
0
    SprintfLiteral(buf,
728
0
                   "LUL frame stats: TOTAL %5u"
729
0
                   "    CTX %4u    CFI %4u    FP %4u",
730
0
                   n_new, n_new_Context, n_new_CFI, n_new_FP);
731
0
    buf[sizeof(buf)-1] = 0;
732
0
    mLog(buf);
733
0
  }
734
0
}
735
736
737
size_t
738
LUL::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
739
0
{
740
0
  size_t n = aMallocSizeOf(this);
741
0
  n += mPriMap->SizeOfIncludingThis(aMallocSizeOf);
742
0
743
0
  // Measurement of the following members may be added later if DMD finds it
744
0
  // is worthwhile:
745
0
  // - mSegArray
746
0
  // - mUSU
747
0
748
0
  return n;
749
0
}
750
751
752
void
753
LUL::EnableUnwinding()
754
0
{
755
0
  LUL_LOG("LUL::EnableUnwinding");
756
0
  // Don't assert for Admin mode here.  That is, tolerate a call here
757
0
  // if we are already in Unwinding mode.
758
0
  MOZ_RELEASE_ASSERT(gettid() == mAdminThreadId);
759
0
760
0
  mAdminMode = false;
761
0
}
762
763
764
void
765
LUL::NotifyAfterMap(uintptr_t aRXavma, size_t aSize,
766
                    const char* aFileName, const void* aMappedImage)
767
0
{
768
0
  MOZ_RELEASE_ASSERT(mAdminMode);
769
0
  MOZ_RELEASE_ASSERT(gettid() == mAdminThreadId);
770
0
771
0
  mLog(":\n");
772
0
  char buf[200];
773
0
  SprintfLiteral(buf, "NotifyMap %llx %llu %s\n",
774
0
                 (unsigned long long int)aRXavma, (unsigned long long int)aSize,
775
0
                 aFileName);
776
0
  buf[sizeof(buf)-1] = 0;
777
0
  mLog(buf);
778
0
779
0
  // Ignore obviously-stupid notifications.
780
0
  if (aSize > 0) {
781
0
782
0
    // Here's a new mapping, for this object.
783
0
    mozilla::UniquePtr<SecMap> smap = mozilla::MakeUnique<SecMap>(mLog);
784
0
785
0
    // Read CFI or EXIDX unwind data into |smap|.
786
0
    if (!aMappedImage) {
787
0
      (void)lul::ReadSymbolData(
788
0
              string(aFileName), std::vector<string>(), smap.get(),
789
0
              (void*)aRXavma, aSize, mUSU, mLog);
790
0
    } else {
791
0
      (void)lul::ReadSymbolDataInternal(
792
0
              (const uint8_t*)aMappedImage,
793
0
              string(aFileName), std::vector<string>(), smap.get(),
794
0
              (void*)aRXavma, aSize, mUSU, mLog);
795
0
    }
796
0
797
0
    mLog("NotifyMap .. preparing entries\n");
798
0
799
0
    smap->PrepareRuleSets(aRXavma, aSize);
800
0
801
0
    SprintfLiteral(buf,
802
0
                   "NotifyMap got %lld entries\n", (long long int)smap->Size());
803
0
    buf[sizeof(buf)-1] = 0;
804
0
    mLog(buf);
805
0
806
0
    // Add it to the primary map (the top level set of mapped objects).
807
0
    mPriMap->AddSecMap(std::move(smap));
808
0
809
0
    // Tell the segment array about the mapping, so that the stack
810
0
    // scan and __kernel_syscall mechanisms know where valid code is.
811
0
    mSegArray->add(aRXavma, aRXavma + aSize - 1, true);
812
0
  }
813
0
}
814
815
816
void
817
LUL::NotifyExecutableArea(uintptr_t aRXavma, size_t aSize)
818
0
{
819
0
  MOZ_RELEASE_ASSERT(mAdminMode);
820
0
  MOZ_RELEASE_ASSERT(gettid() == mAdminThreadId);
821
0
822
0
  mLog(":\n");
823
0
  char buf[200];
824
0
  SprintfLiteral(buf, "NotifyExecutableArea %llx %llu\n",
825
0
                   (unsigned long long int)aRXavma, (unsigned long long int)aSize);
826
0
  buf[sizeof(buf)-1] = 0;
827
0
  mLog(buf);
828
0
829
0
  // Ignore obviously-stupid notifications.
830
0
  if (aSize > 0) {
831
0
    // Tell the segment array about the mapping, so that the stack
832
0
    // scan and __kernel_syscall mechanisms know where valid code is.
833
0
    mSegArray->add(aRXavma, aRXavma + aSize - 1, true);
834
0
  }
835
0
}
836
837
838
void
839
LUL::NotifyBeforeUnmap(uintptr_t aRXavmaMin, uintptr_t aRXavmaMax)
840
0
{
841
0
  MOZ_RELEASE_ASSERT(mAdminMode);
842
0
  MOZ_RELEASE_ASSERT(gettid() == mAdminThreadId);
843
0
844
0
  mLog(":\n");
845
0
  char buf[100];
846
0
  SprintfLiteral(buf, "NotifyUnmap %016llx-%016llx\n",
847
0
                 (unsigned long long int)aRXavmaMin,
848
0
                 (unsigned long long int)aRXavmaMax);
849
0
  buf[sizeof(buf)-1] = 0;
850
0
  mLog(buf);
851
0
852
0
  MOZ_ASSERT(aRXavmaMin <= aRXavmaMax);
853
0
854
0
  // Remove from the primary map, any secondary maps that intersect
855
0
  // with the address range.  Also delete the secondary maps.
856
0
  mPriMap->RemoveSecMapsInRange(aRXavmaMin, aRXavmaMax);
857
0
858
0
  // Tell the segment array that the address range no longer
859
0
  // contains valid code.
860
0
  mSegArray->add(aRXavmaMin, aRXavmaMax, false);
861
0
862
0
  SprintfLiteral(buf, "NotifyUnmap: now have %d SecMaps\n",
863
0
                 (int)mPriMap->CountSecMaps());
864
0
  buf[sizeof(buf)-1] = 0;
865
0
  mLog(buf);
866
0
}
867
868
869
size_t
870
LUL::CountMappings()
871
0
{
872
0
  MOZ_RELEASE_ASSERT(mAdminMode);
873
0
  MOZ_RELEASE_ASSERT(gettid() == mAdminThreadId);
874
0
875
0
  return mPriMap->CountSecMaps();
876
0
}
877
878
879
// RUNS IN NO-MALLOC CONTEXT
880
static
881
TaggedUWord DerefTUW(TaggedUWord aAddr, const StackImage* aStackImg)
882
0
{
883
0
  if (!aAddr.Valid()) {
884
0
    return TaggedUWord();
885
0
  }
886
0
887
0
  // Lower limit check.  |aAddr.Value()| is the lowest requested address
888
0
  // and |aStackImg->mStartAvma| is the lowest address we actually have,
889
0
  // so the comparison is straightforward.
890
0
  if (aAddr.Value() < aStackImg->mStartAvma) {
891
0
    return TaggedUWord();
892
0
  }
893
0
894
0
  // Upper limit check.  We must compute the highest requested address
895
0
  // and the highest address we actually have, but being careful to
896
0
  // avoid overflow.  In particular if |aAddr| is 0xFFF...FFF or the
897
0
  // 3/7 values below that, then we will get overflow.  See bug #1245477.
898
0
  typedef CheckedInt<uintptr_t> CheckedUWord;
899
0
  CheckedUWord highest_requested_plus_one
900
0
    = CheckedUWord(aAddr.Value()) + CheckedUWord(sizeof(uintptr_t));
901
0
  CheckedUWord highest_available_plus_one
902
0
    = CheckedUWord(aStackImg->mStartAvma) + CheckedUWord(aStackImg->mLen);
903
0
  if (!highest_requested_plus_one.isValid()     // overflow?
904
0
      || !highest_available_plus_one.isValid()  // overflow?
905
0
      || (highest_requested_plus_one.value()
906
0
          > highest_available_plus_one.value())) { // in range?
907
0
    return TaggedUWord();
908
0
  }
909
0
910
0
  return TaggedUWord(*(uintptr_t*)(
911
0
           &aStackImg->mContents[aAddr.Value() - aStackImg->mStartAvma]));
912
0
}
913
914
// RUNS IN NO-MALLOC CONTEXT
915
static
916
TaggedUWord EvaluateReg(int16_t aReg, const UnwindRegs* aOldRegs,
917
                        TaggedUWord aCFA)
918
0
{
919
0
  switch (aReg) {
920
0
    case DW_REG_CFA:       return aCFA;
921
0
#if defined(GP_ARCH_amd64) || defined(GP_ARCH_x86)
922
0
    case DW_REG_INTEL_XBP: return aOldRegs->xbp;
923
0
    case DW_REG_INTEL_XSP: return aOldRegs->xsp;
924
0
    case DW_REG_INTEL_XIP: return aOldRegs->xip;
925
#elif defined(GP_ARCH_arm)
926
    case DW_REG_ARM_R7:    return aOldRegs->r7;
927
    case DW_REG_ARM_R11:   return aOldRegs->r11;
928
    case DW_REG_ARM_R12:   return aOldRegs->r12;
929
    case DW_REG_ARM_R13:   return aOldRegs->r13;
930
    case DW_REG_ARM_R14:   return aOldRegs->r14;
931
    case DW_REG_ARM_R15:   return aOldRegs->r15;
932
#elif defined(GP_ARCH_arm64)
933
    case DW_REG_AARCH64_X29: return aOldRegs->x29;
934
    case DW_REG_AARCH64_X30: return aOldRegs->x30;
935
    case DW_REG_AARCH64_SP:  return aOldRegs->sp;
936
#elif defined(GP_ARCH_mips64)
937
    case DW_REG_MIPS_SP:   return aOldRegs->sp;
938
    case DW_REG_MIPS_FP:   return aOldRegs->fp;
939
    case DW_REG_MIPS_PC:   return aOldRegs->pc;
940
#else
941
# error "Unsupported arch"
942
#endif
943
0
    default: MOZ_ASSERT(0); return TaggedUWord();
944
0
  }
945
0
}
946
947
// RUNS IN NO-MALLOC CONTEXT
948
// See prototype for comment.
949
TaggedUWord EvaluatePfxExpr(int32_t start,
950
                            const UnwindRegs* aOldRegs,
951
                            TaggedUWord aCFA, const StackImage* aStackImg,
952
                            const vector<PfxInstr>& aPfxInstrs)
953
0
{
954
0
  // A small evaluation stack, and a stack pointer, which points to
955
0
  // the highest numbered in-use element.
956
0
  const int N_STACK = 10;
957
0
  TaggedUWord stack[N_STACK];
958
0
  int stackPointer = -1;
959
0
  for (int i = 0; i < N_STACK; i++)
960
0
    stack[i] = TaggedUWord();
961
0
962
0
# define PUSH(_tuw) \
963
0
    do { \
964
0
      if (stackPointer >= N_STACK-1) goto fail; /* overflow */ \
965
0
      stack[++stackPointer] = (_tuw); \
966
0
    } while (0)
967
0
968
0
# define POP(_lval) \
969
0
    do { \
970
0
      if (stackPointer < 0) goto fail; /* underflow */ \
971
0
     _lval = stack[stackPointer--]; \
972
0
   } while (0)
973
0
974
0
  // Cursor in the instruction sequence.
975
0
  size_t curr = start + 1;
976
0
977
0
  // Check the start point is sane.
978
0
  size_t nInstrs = aPfxInstrs.size();
979
0
  if (start < 0 || (size_t)start >= nInstrs)
980
0
    goto fail;
981
0
982
0
  {
983
0
    // The instruction sequence must start with PX_Start.  If not,
984
0
    // something is seriously wrong.
985
0
    PfxInstr first = aPfxInstrs[start];
986
0
    if (first.mOpcode != PX_Start)
987
0
      goto fail;
988
0
989
0
    // Push the CFA on the stack to start with (or not), as required by
990
0
    // the original DW_OP_*expression* CFI.
991
0
    if (first.mOperand != 0)
992
0
      PUSH(aCFA);
993
0
  }
994
0
995
0
  while (true) {
996
0
    if (curr >= nInstrs)
997
0
      goto fail; // ran off the end of the sequence
998
0
999
0
    PfxInstr pfxi = aPfxInstrs[curr++];
1000
0
    if (pfxi.mOpcode == PX_End)
1001
0
      break; // we're done
1002
0
1003
0
    switch (pfxi.mOpcode) {
1004
0
      case PX_Start:
1005
0
        // This should appear only at the start of the sequence.
1006
0
        goto fail;
1007
0
      case PX_End:
1008
0
        // We just took care of that, so we shouldn't see it again.
1009
0
        MOZ_ASSERT(0);
1010
0
        goto fail;
1011
0
      case PX_SImm32:
1012
0
        PUSH(TaggedUWord((intptr_t)pfxi.mOperand));
1013
0
        break;
1014
0
      case PX_DwReg: {
1015
0
        DW_REG_NUMBER reg = (DW_REG_NUMBER)pfxi.mOperand;
1016
0
        MOZ_ASSERT(reg != DW_REG_CFA);
1017
0
        PUSH(EvaluateReg(reg, aOldRegs, aCFA));
1018
0
        break;
1019
0
      }
1020
0
      case PX_Deref: {
1021
0
        TaggedUWord addr;
1022
0
        POP(addr);
1023
0
        PUSH(DerefTUW(addr, aStackImg));
1024
0
        break;
1025
0
      }
1026
0
      case PX_Add: {
1027
0
        TaggedUWord x, y;
1028
0
        POP(x); POP(y); PUSH(y + x);
1029
0
        break;
1030
0
      }
1031
0
      case PX_Sub: {
1032
0
        TaggedUWord x, y;
1033
0
        POP(x); POP(y); PUSH(y - x);
1034
0
        break;
1035
0
      }
1036
0
      case PX_And: {
1037
0
        TaggedUWord x, y;
1038
0
        POP(x); POP(y); PUSH(y & x);
1039
0
        break;
1040
0
      }
1041
0
      case PX_Or: {
1042
0
        TaggedUWord x, y;
1043
0
        POP(x); POP(y); PUSH(y | x);
1044
0
        break;
1045
0
      }
1046
0
      case PX_CmpGES: {
1047
0
        TaggedUWord x, y;
1048
0
        POP(x); POP(y); PUSH(y.CmpGEs(x));
1049
0
        break;
1050
0
      }
1051
0
      case PX_Shl: {
1052
0
        TaggedUWord x, y;
1053
0
        POP(x); POP(y); PUSH(y << x);
1054
0
        break;
1055
0
      }
1056
0
      default:
1057
0
        MOZ_ASSERT(0);
1058
0
        goto fail;
1059
0
    }
1060
0
  } // while (true)
1061
0
1062
0
  // Evaluation finished.  The top value on the stack is the result.
1063
0
  if (stackPointer >= 0) {
1064
0
    return stack[stackPointer];
1065
0
  }
1066
0
  // Else fall through
1067
0
1068
0
 fail:
1069
0
  return TaggedUWord();
1070
0
1071
0
# undef PUSH
1072
0
# undef POP
1073
0
}
1074
1075
// RUNS IN NO-MALLOC CONTEXT
1076
TaggedUWord LExpr::EvaluateExpr(const UnwindRegs* aOldRegs,
1077
                                TaggedUWord aCFA, const StackImage* aStackImg,
1078
                                const vector<PfxInstr>* aPfxInstrs) const
1079
0
{
1080
0
  switch (mHow) {
1081
0
    case UNKNOWN:
1082
0
      return TaggedUWord();
1083
0
    case NODEREF: {
1084
0
      TaggedUWord tuw = EvaluateReg(mReg, aOldRegs, aCFA);
1085
0
      tuw = tuw + TaggedUWord((intptr_t)mOffset);
1086
0
      return tuw;
1087
0
    }
1088
0
    case DEREF: {
1089
0
      TaggedUWord tuw = EvaluateReg(mReg, aOldRegs, aCFA);
1090
0
      tuw = tuw + TaggedUWord((intptr_t)mOffset);
1091
0
      return DerefTUW(tuw, aStackImg);
1092
0
    }
1093
0
    case PFXEXPR: {
1094
0
      MOZ_ASSERT(aPfxInstrs);
1095
0
      if (!aPfxInstrs) {
1096
0
        return TaggedUWord();
1097
0
      }
1098
0
      return EvaluatePfxExpr(mOffset, aOldRegs, aCFA, aStackImg, *aPfxInstrs);
1099
0
    }
1100
0
    default:
1101
0
      MOZ_ASSERT(0);
1102
0
      return TaggedUWord();
1103
0
  }
1104
0
}
1105
1106
// RUNS IN NO-MALLOC CONTEXT
1107
static
1108
void UseRuleSet(/*MOD*/UnwindRegs* aRegs,
1109
                const StackImage* aStackImg, const RuleSet* aRS,
1110
                const vector<PfxInstr>* aPfxInstrs)
1111
0
{
1112
0
  // Take a copy of regs, since we'll need to refer to the old values
1113
0
  // whilst computing the new ones.
1114
0
  UnwindRegs old_regs = *aRegs;
1115
0
1116
0
  // Mark all the current register values as invalid, so that the
1117
0
  // caller can see, on our return, which ones have been computed
1118
0
  // anew.  If we don't even manage to compute a new PC value, then
1119
0
  // the caller will have to abandon the unwind.
1120
0
  // FIXME: Create and use instead: aRegs->SetAllInvalid();
1121
0
#if defined(GP_ARCH_amd64) || defined(GP_ARCH_x86)
1122
0
  aRegs->xbp = TaggedUWord();
1123
0
  aRegs->xsp = TaggedUWord();
1124
0
  aRegs->xip = TaggedUWord();
1125
#elif defined(GP_ARCH_arm)
1126
  aRegs->r7  = TaggedUWord();
1127
  aRegs->r11 = TaggedUWord();
1128
  aRegs->r12 = TaggedUWord();
1129
  aRegs->r13 = TaggedUWord();
1130
  aRegs->r14 = TaggedUWord();
1131
  aRegs->r15 = TaggedUWord();
1132
#elif defined(GP_ARCH_arm64)
1133
  aRegs->x29 = TaggedUWord();
1134
  aRegs->x30 = TaggedUWord();
1135
  aRegs->sp  = TaggedUWord();
1136
  aRegs->pc  = TaggedUWord();
1137
#elif defined(GP_ARCH_mips64)
1138
  aRegs->sp  = TaggedUWord();
1139
  aRegs->fp  = TaggedUWord();
1140
  aRegs->pc  = TaggedUWord();
1141
#else
1142
#  error "Unsupported arch"
1143
#endif
1144
1145
0
  // This is generally useful.
1146
0
  const TaggedUWord inval = TaggedUWord();
1147
0
1148
0
  // First, compute the CFA.
1149
0
  TaggedUWord cfa
1150
0
    = aRS->mCfaExpr.EvaluateExpr(&old_regs,
1151
0
                                 inval/*old cfa*/, aStackImg, aPfxInstrs);
1152
0
1153
0
  // If we didn't manage to compute the CFA, well .. that's ungood,
1154
0
  // but keep going anyway.  It'll be OK provided none of the register
1155
0
  // value rules mention the CFA.  In any case, compute the new values
1156
0
  // for each register that we're tracking.
1157
0
1158
0
#if defined(GP_ARCH_amd64) || defined(GP_ARCH_x86)
1159
0
  aRegs->xbp
1160
0
    = aRS->mXbpExpr.EvaluateExpr(&old_regs, cfa, aStackImg, aPfxInstrs);
1161
0
  aRegs->xsp
1162
0
    = aRS->mXspExpr.EvaluateExpr(&old_regs, cfa, aStackImg, aPfxInstrs);
1163
0
  aRegs->xip
1164
0
    = aRS->mXipExpr.EvaluateExpr(&old_regs, cfa, aStackImg, aPfxInstrs);
1165
#elif defined(GP_ARCH_arm)
1166
  aRegs->r7
1167
    = aRS->mR7expr .EvaluateExpr(&old_regs, cfa, aStackImg, aPfxInstrs);
1168
  aRegs->r11
1169
    = aRS->mR11expr.EvaluateExpr(&old_regs, cfa, aStackImg, aPfxInstrs);
1170
  aRegs->r12
1171
    = aRS->mR12expr.EvaluateExpr(&old_regs, cfa, aStackImg, aPfxInstrs);
1172
  aRegs->r13
1173
    = aRS->mR13expr.EvaluateExpr(&old_regs, cfa, aStackImg, aPfxInstrs);
1174
  aRegs->r14
1175
    = aRS->mR14expr.EvaluateExpr(&old_regs, cfa, aStackImg, aPfxInstrs);
1176
  aRegs->r15
1177
    = aRS->mR15expr.EvaluateExpr(&old_regs, cfa, aStackImg, aPfxInstrs);
1178
#elif defined(GP_ARCH_arm64)
1179
  aRegs->x29
1180
    = aRS->mX29expr.EvaluateExpr(&old_regs, cfa, aStackImg, aPfxInstrs);
1181
  aRegs->x30
1182
    = aRS->mX30expr.EvaluateExpr(&old_regs, cfa, aStackImg, aPfxInstrs);
1183
  aRegs->sp
1184
    = aRS->mSPexpr.EvaluateExpr(&old_regs, cfa, aStackImg, aPfxInstrs);
1185
#elif defined(GP_ARCH_mips64)
1186
  aRegs->sp
1187
    = aRS->mSPexpr.EvaluateExpr(&old_regs, cfa, aStackImg, aPfxInstrs);
1188
  aRegs->fp
1189
    = aRS->mFPexpr.EvaluateExpr(&old_regs, cfa, aStackImg, aPfxInstrs);
1190
  aRegs->pc
1191
    = aRS->mPCexpr.EvaluateExpr(&old_regs, cfa, aStackImg, aPfxInstrs);
1192
#else
1193
# error "Unsupported arch"
1194
#endif
1195
1196
0
  // We're done.  Any regs for which we didn't manage to compute a
1197
0
  // new value will now be marked as invalid.
1198
0
}
1199
1200
// RUNS IN NO-MALLOC CONTEXT
1201
void
1202
LUL::Unwind(/*OUT*/uintptr_t* aFramePCs,
1203
            /*OUT*/uintptr_t* aFrameSPs,
1204
            /*OUT*/size_t* aFramesUsed,
1205
            /*OUT*/size_t* aFramePointerFramesAcquired,
1206
            size_t aFramesAvail,
1207
            UnwindRegs* aStartRegs, StackImage* aStackImg)
1208
0
{
1209
0
  MOZ_RELEASE_ASSERT(!mAdminMode);
1210
0
1211
0
  /////////////////////////////////////////////////////////
1212
0
  // BEGIN UNWIND
1213
0
1214
0
  *aFramesUsed = 0;
1215
0
1216
0
  UnwindRegs  regs          = *aStartRegs;
1217
0
  TaggedUWord last_valid_sp = TaggedUWord();
1218
0
1219
0
  while (true) {
1220
0
1221
0
    if (DEBUG_MAIN) {
1222
0
      char buf[300];
1223
0
      mLog("\n");
1224
0
#if defined(GP_ARCH_amd64) || defined(GP_ARCH_x86)
1225
0
      SprintfLiteral(buf,
1226
0
                     "LoopTop: rip %d/%llx  rsp %d/%llx  rbp %d/%llx\n",
1227
0
                     (int)regs.xip.Valid(), (unsigned long long int)regs.xip.Value(),
1228
0
                     (int)regs.xsp.Valid(), (unsigned long long int)regs.xsp.Value(),
1229
0
                     (int)regs.xbp.Valid(), (unsigned long long int)regs.xbp.Value());
1230
0
      buf[sizeof(buf)-1] = 0;
1231
0
      mLog(buf);
1232
#elif defined(GP_ARCH_arm)
1233
      SprintfLiteral(buf,
1234
                     "LoopTop: r15 %d/%llx  r7 %d/%llx  r11 %d/%llx"
1235
                     "  r12 %d/%llx  r13 %d/%llx  r14 %d/%llx\n",
1236
                     (int)regs.r15.Valid(), (unsigned long long int)regs.r15.Value(),
1237
                     (int)regs.r7.Valid(),  (unsigned long long int)regs.r7.Value(),
1238
                     (int)regs.r11.Valid(), (unsigned long long int)regs.r11.Value(),
1239
                     (int)regs.r12.Valid(), (unsigned long long int)regs.r12.Value(),
1240
                     (int)regs.r13.Valid(), (unsigned long long int)regs.r13.Value(),
1241
                     (int)regs.r14.Valid(), (unsigned long long int)regs.r14.Value());
1242
      buf[sizeof(buf)-1] = 0;
1243
      mLog(buf);
1244
#elif defined(GP_ARCH_arm64)
1245
      SprintfLiteral(buf,
1246
                     "LoopTop: pc %d/%llx  x29 %d/%llx  x30 %d/%llx"
1247
                     "  sp %d/%llx\n",
1248
                     (int)regs.pc.Valid(), (unsigned long long int)regs.pc.Value(),
1249
                     (int)regs.x29.Valid(), (unsigned long long int)regs.x29.Value(),
1250
                     (int)regs.x30.Valid(), (unsigned long long int)regs.x30.Value(),
1251
                     (int)regs.sp.Valid(), (unsigned long long int)regs.sp.Value());
1252
      buf[sizeof(buf)-1] = 0;
1253
      mLog(buf);
1254
#elif defined(GP_ARCH_mips64)
1255
      SprintfLiteral(buf,
1256
                     "LoopTop: pc %d/%llx  sp %d/%llx  fp %d/%llx\n",
1257
                     (int)regs.pc.Valid(), (unsigned long long int)regs.pc.Value(),
1258
                     (int)regs.sp.Valid(), (unsigned long long int)regs.sp.Value(),
1259
                     (int)regs.fp.Valid(), (unsigned long long int)regs.fp.Value());
1260
      buf[sizeof(buf)-1] = 0;
1261
      mLog(buf);
1262
#else
1263
# error "Unsupported arch"
1264
#endif
1265
    }
1266
0
1267
0
#if defined(GP_ARCH_amd64) || defined(GP_ARCH_x86)
1268
0
    TaggedUWord ia = regs.xip;
1269
0
    TaggedUWord sp = regs.xsp;
1270
#elif defined(GP_ARCH_arm)
1271
    TaggedUWord ia = (*aFramesUsed == 0 ? regs.r15 : regs.r14);
1272
    TaggedUWord sp = regs.r13;
1273
#elif defined(GP_ARCH_arm64)
1274
    TaggedUWord ia = (*aFramesUsed == 0 ? regs.pc : regs.x30);
1275
    TaggedUWord sp = regs.sp;
1276
#elif defined(GP_ARCH_mips64)
1277
    TaggedUWord ia = regs.pc;
1278
    TaggedUWord sp = regs.sp;
1279
#else
1280
# error "Unsupported arch"
1281
#endif
1282
1283
0
    if (*aFramesUsed >= aFramesAvail) {
1284
0
      break;
1285
0
    }
1286
0
1287
0
    // If we don't have a valid value for the PC, give up.
1288
0
    if (!ia.Valid()) {
1289
0
      break;
1290
0
    }
1291
0
1292
0
    // If this is the innermost frame, record the SP value, which
1293
0
    // presumably is valid.  If this isn't the innermost frame, and we
1294
0
    // have a valid SP value, check that its SP value isn't less that
1295
0
    // the one we've seen so far, so as to catch potential SP value
1296
0
    // cycles.
1297
0
    if (*aFramesUsed == 0) {
1298
0
      last_valid_sp = sp;
1299
0
    } else {
1300
0
      MOZ_ASSERT(last_valid_sp.Valid());
1301
0
      if (sp.Valid()) {
1302
0
        if (sp.Value() < last_valid_sp.Value()) {
1303
0
          // Hmm, SP going in the wrong direction.  Let's stop.
1304
0
          break;
1305
0
        }
1306
0
        // Remember where we got to.
1307
0
        last_valid_sp = sp;
1308
0
      }
1309
0
    }
1310
0
1311
0
    // For the innermost frame, the IA value is what we need.  For all
1312
0
    // other frames, it's actually the return address, so back up one
1313
0
    // byte so as to get it into the calling instruction.
1314
0
    aFramePCs[*aFramesUsed] = ia.Value() - (*aFramesUsed == 0 ? 0 : 1);
1315
0
    aFrameSPs[*aFramesUsed] = sp.Valid() ? sp.Value() : 0;
1316
0
    (*aFramesUsed)++;
1317
0
1318
0
    // Find the RuleSet for the current IA, if any.  This will also
1319
0
    // query the backing (secondary) maps if it isn't found in the
1320
0
    // thread-local cache.
1321
0
1322
0
    // If this isn't the innermost frame, back up into the calling insn.
1323
0
    if (*aFramesUsed > 1) {
1324
0
      ia = ia + TaggedUWord((uintptr_t)(-1));
1325
0
    }
1326
0
1327
0
    pair<const RuleSet*, const vector<PfxInstr>*> ruleset_and_pfxinstrs
1328
0
      = mPriMap->Lookup(ia.Value());
1329
0
    const RuleSet* ruleset = ruleset_and_pfxinstrs.first;
1330
0
    const vector<PfxInstr>* pfxinstrs = ruleset_and_pfxinstrs.second;
1331
0
1332
0
    if (DEBUG_MAIN) {
1333
0
      char buf[100];
1334
0
      SprintfLiteral(buf, "ruleset for 0x%llx = %p\n",
1335
0
                     (unsigned long long int)ia.Value(), ruleset);
1336
0
      buf[sizeof(buf)-1] = 0;
1337
0
      mLog(buf);
1338
0
    }
1339
0
1340
#if defined(GP_PLAT_x86_android) || defined(GP_PLAT_x86_linux)
1341
    /////////////////////////////////////////////
1342
    ////
1343
    // On 32 bit x86-linux, syscalls are often done via the VDSO
1344
    // function __kernel_vsyscall, which doesn't have a corresponding
1345
    // object that we can read debuginfo from.  That effectively kills
1346
    // off all stack traces for threads blocked in syscalls.  Hence
1347
    // special-case by looking at the code surrounding the program
1348
    // counter.
1349
    //
1350
    // 0xf7757420 <__kernel_vsyscall+0>:  push   %ecx
1351
    // 0xf7757421 <__kernel_vsyscall+1>:  push   %edx
1352
    // 0xf7757422 <__kernel_vsyscall+2>:  push   %ebp
1353
    // 0xf7757423 <__kernel_vsyscall+3>:  mov    %esp,%ebp
1354
    // 0xf7757425 <__kernel_vsyscall+5>:  sysenter
1355
    // 0xf7757427 <__kernel_vsyscall+7>:  nop
1356
    // 0xf7757428 <__kernel_vsyscall+8>:  nop
1357
    // 0xf7757429 <__kernel_vsyscall+9>:  nop
1358
    // 0xf775742a <__kernel_vsyscall+10>: nop
1359
    // 0xf775742b <__kernel_vsyscall+11>: nop
1360
    // 0xf775742c <__kernel_vsyscall+12>: nop
1361
    // 0xf775742d <__kernel_vsyscall+13>: nop
1362
    // 0xf775742e <__kernel_vsyscall+14>: int    $0x80
1363
    // 0xf7757430 <__kernel_vsyscall+16>: pop    %ebp
1364
    // 0xf7757431 <__kernel_vsyscall+17>: pop    %edx
1365
    // 0xf7757432 <__kernel_vsyscall+18>: pop    %ecx
1366
    // 0xf7757433 <__kernel_vsyscall+19>: ret
1367
    //
1368
    // In cases where the sampled thread is blocked in a syscall, its
1369
    // program counter will point at "pop %ebp".  Hence we look for
1370
    // the sequence "int $0x80; pop %ebp; pop %edx; pop %ecx; ret", and
1371
    // the corresponding register-recovery actions are:
1372
    //    new_ebp = *(old_esp + 0)
1373
    //    new eip = *(old_esp + 12)
1374
    //    new_esp = old_esp + 16
1375
    //
1376
    // It may also be the case that the program counter points two
1377
    // nops before the "int $0x80", viz, is __kernel_vsyscall+12, in
1378
    // the case where the syscall has been restarted but the thread
1379
    // hasn't been rescheduled.  The code below doesn't handle that;
1380
    // it could easily be made to.
1381
    //
1382
    if (!ruleset && *aFramesUsed == 1 && ia.Valid() && sp.Valid()) {
1383
      uintptr_t insns_min, insns_max;
1384
      uintptr_t eip = ia.Value();
1385
      bool b = mSegArray->getBoundingCodeSegment(&insns_min, &insns_max, eip);
1386
      if (b && eip - 2 >= insns_min && eip + 3 <= insns_max) {
1387
        uint8_t* eipC = (uint8_t*)eip;
1388
        if (eipC[-2] == 0xCD && eipC[-1] == 0x80 && eipC[0] == 0x5D &&
1389
            eipC[1] == 0x5A && eipC[2] == 0x59 && eipC[3] == 0xC3) {
1390
          TaggedUWord sp_plus_0  = sp;
1391
          TaggedUWord sp_plus_12 = sp;
1392
          TaggedUWord sp_plus_16 = sp;
1393
          sp_plus_12 = sp_plus_12 + TaggedUWord(12);
1394
          sp_plus_16 = sp_plus_16 + TaggedUWord(16);
1395
          TaggedUWord new_ebp = DerefTUW(sp_plus_0, aStackImg);
1396
          TaggedUWord new_eip = DerefTUW(sp_plus_12, aStackImg);
1397
          TaggedUWord new_esp = sp_plus_16;
1398
          if (new_ebp.Valid() && new_eip.Valid() && new_esp.Valid()) {
1399
            regs.xbp = new_ebp;
1400
            regs.xip = new_eip;
1401
            regs.xsp = new_esp;
1402
            continue;
1403
          }
1404
        }
1405
      }
1406
    }
1407
    ////
1408
    /////////////////////////////////////////////
1409
#endif // defined(GP_PLAT_x86_android) || defined(GP_PLAT_x86_linux)
1410
1411
0
    // So, do we have a ruleset for this address?  If so, use it now.
1412
0
    if (ruleset) {
1413
0
1414
0
      if (DEBUG_MAIN) {
1415
0
        ruleset->Print(mLog); mLog("\n");
1416
0
      }
1417
0
      // Use the RuleSet to compute the registers for the previous
1418
0
      // frame.  |regs| is modified in-place.
1419
0
      UseRuleSet(&regs, aStackImg, ruleset, pfxinstrs);
1420
0
      continue;
1421
0
1422
0
    }
1423
0
1424
0
#if defined(GP_PLAT_amd64_linux) || defined(GP_PLAT_x86_linux)
1425
0
    // There's no RuleSet for the specified address.  On amd64/x86_linux, see if
1426
0
    // it's possible to recover the caller's frame by using the frame pointer.
1427
0
1428
0
    // We seek to compute (new_IP, new_SP, new_BP) from (old_BP, stack image),
1429
0
    // and assume the following layout:
1430
0
    //
1431
0
    //                 <--- new_SP
1432
0
    //   +----------+
1433
0
    //   |  new_IP  |  (return address)
1434
0
    //   +----------+
1435
0
    //   |  new_BP  |  <--- old_BP
1436
0
    //   +----------+
1437
0
    //   |   ....   |
1438
0
    //   |   ....   |
1439
0
    //   |   ....   |
1440
0
    //   +----------+  <---- old_SP (arbitrary, but must be <= old_BP)
1441
0
1442
0
    const size_t wordSzB = sizeof(uintptr_t);
1443
0
    TaggedUWord old_xsp = regs.xsp;
1444
0
1445
0
    // points at new_BP ?
1446
0
    TaggedUWord old_xbp = regs.xbp;
1447
0
    // points at new_IP ?
1448
0
    TaggedUWord old_xbp_plus1 = regs.xbp + TaggedUWord(1 * wordSzB);
1449
0
    // is the new_SP ?
1450
0
    TaggedUWord old_xbp_plus2 = regs.xbp + TaggedUWord(2 * wordSzB);
1451
0
1452
0
    if (old_xbp.Valid() && old_xbp.IsAligned() &&
1453
0
        old_xsp.Valid() && old_xsp.IsAligned() &&
1454
0
        old_xsp.Value() <= old_xbp.Value()) {
1455
0
      // We don't need to do any range, alignment or validity checks for
1456
0
      // addresses passed to DerefTUW, since that performs them itself, and
1457
0
      // returns an invalid value on failure.  Any such value will poison
1458
0
      // subsequent uses, and we do a final check for validity before putting
1459
0
      // the computed values into |regs|.
1460
0
      TaggedUWord new_xbp = DerefTUW(old_xbp, aStackImg);
1461
0
      if (new_xbp.Valid() && new_xbp.IsAligned() &&
1462
0
          old_xbp.Value() < new_xbp.Value()) {
1463
0
        TaggedUWord new_xip = DerefTUW(old_xbp_plus1, aStackImg);
1464
0
        TaggedUWord new_xsp = old_xbp_plus2;
1465
0
        if (new_xbp.Valid() && new_xip.Valid() && new_xsp.Valid()) {
1466
0
          regs.xbp = new_xbp;
1467
0
          regs.xip = new_xip;
1468
0
          regs.xsp = new_xsp;
1469
0
          (*aFramePointerFramesAcquired)++;
1470
0
          continue;
1471
0
        }
1472
0
      }
1473
0
    }
1474
0
#endif // defined(GP_PLAT_amd64_linux) || defined(GP_PLAT_x86_linux)
1475
0
1476
0
    // We failed to recover a frame either using CFI or FP chasing, and we
1477
0
    // have no other ways to recover the frame.  So we have to give up.
1478
0
    break;
1479
0
1480
0
  } // top level unwind loop
1481
0
1482
0
  // END UNWIND
1483
0
  /////////////////////////////////////////////////////////
1484
0
}
1485
1486
1487
////////////////////////////////////////////////////////////////
1488
// LUL Unit Testing                                           //
1489
////////////////////////////////////////////////////////////////
1490
1491
static const int LUL_UNIT_TEST_STACK_SIZE = 32768;
1492
1493
#if defined(GP_ARCH_mips64)
1494
static __attribute__((noinline))
1495
unsigned long __getpc(void) {
1496
    unsigned long rtaddr;
1497
    __asm__ volatile ("move %0, $31" : "=r"(rtaddr));
1498
    return rtaddr;
1499
}
1500
#endif
1501
1502
// This function is innermost in the test call sequence.  It uses LUL
1503
// to unwind, and compares the result with the sequence specified in
1504
// the director string.  These need to agree in order for the test to
1505
// pass.  In order not to screw up the results, this function needs
1506
// to have a not-very big stack frame, since we're only presenting
1507
// the innermost LUL_UNIT_TEST_STACK_SIZE bytes of stack to LUL, and
1508
// that chunk unavoidably includes the frame for this function.
1509
//
1510
// This function must not be inlined into its callers.  Doing so will
1511
// cause the expected-vs-actual backtrace consistency checking to
1512
// fail.  Prints summary results to |aLUL|'s logging sink and also
1513
// returns a boolean indicating whether or not the test passed.
1514
static __attribute__((noinline))
1515
bool GetAndCheckStackTrace(LUL* aLUL, const char* dstring)
1516
0
{
1517
0
  // Get hold of the current unwind-start registers.
1518
0
  UnwindRegs startRegs;
1519
0
  memset(&startRegs, 0, sizeof(startRegs));
1520
0
#if defined(GP_PLAT_amd64_linux)
1521
0
  volatile uintptr_t block[3];
1522
0
  MOZ_ASSERT(sizeof(block) == 24);
1523
0
  __asm__ __volatile__(
1524
0
    "leaq 0(%%rip), %%r15"   "\n\t"
1525
0
    "movq %%r15, 0(%0)"      "\n\t"
1526
0
    "movq %%rsp, 8(%0)"      "\n\t"
1527
0
    "movq %%rbp, 16(%0)"     "\n"
1528
0
    : : "r"(&block[0]) : "memory", "r15"
1529
0
  );
1530
0
  startRegs.xip = TaggedUWord(block[0]);
1531
0
  startRegs.xsp = TaggedUWord(block[1]);
1532
0
  startRegs.xbp = TaggedUWord(block[2]);
1533
0
  const uintptr_t REDZONE_SIZE = 128;
1534
0
  uintptr_t start = block[1] - REDZONE_SIZE;
1535
#elif defined(GP_PLAT_x86_linux) || defined(GP_PLAT_x86_android)
1536
  volatile uintptr_t block[3];
1537
  MOZ_ASSERT(sizeof(block) == 12);
1538
  __asm__ __volatile__(
1539
    ".byte 0xE8,0x00,0x00,0x00,0x00"/*call next insn*/  "\n\t"
1540
    "popl %%edi"             "\n\t"
1541
    "movl %%edi, 0(%0)"      "\n\t"
1542
    "movl %%esp, 4(%0)"      "\n\t"
1543
    "movl %%ebp, 8(%0)"      "\n"
1544
    : : "r"(&block[0]) : "memory", "edi"
1545
  );
1546
  startRegs.xip = TaggedUWord(block[0]);
1547
  startRegs.xsp = TaggedUWord(block[1]);
1548
  startRegs.xbp = TaggedUWord(block[2]);
1549
  const uintptr_t REDZONE_SIZE = 0;
1550
  uintptr_t start = block[1] - REDZONE_SIZE;
1551
#elif defined(GP_PLAT_arm_linux) || defined(GP_PLAT_arm_android)
1552
  volatile uintptr_t block[6];
1553
  MOZ_ASSERT(sizeof(block) == 24);
1554
  __asm__ __volatile__(
1555
    "mov r0, r15"            "\n\t"
1556
    "str r0,  [%0, #0]"      "\n\t"
1557
    "str r14, [%0, #4]"      "\n\t"
1558
    "str r13, [%0, #8]"      "\n\t"
1559
    "str r12, [%0, #12]"     "\n\t"
1560
    "str r11, [%0, #16]"     "\n\t"
1561
    "str r7,  [%0, #20]"     "\n"
1562
    : : "r"(&block[0]) : "memory", "r0"
1563
  );
1564
  startRegs.r15 = TaggedUWord(block[0]);
1565
  startRegs.r14 = TaggedUWord(block[1]);
1566
  startRegs.r13 = TaggedUWord(block[2]);
1567
  startRegs.r12 = TaggedUWord(block[3]);
1568
  startRegs.r11 = TaggedUWord(block[4]);
1569
  startRegs.r7  = TaggedUWord(block[5]);
1570
  const uintptr_t REDZONE_SIZE = 0;
1571
  uintptr_t start = block[1] - REDZONE_SIZE;
1572
#elif defined(GP_ARCH_arm64)
1573
  volatile uintptr_t block[4];
1574
  MOZ_ASSERT(sizeof(block) == 32);
1575
  __asm__ __volatile__(
1576
    "adr x0, . \n\t"
1577
    "str x0, [%0, #0] \n\t"
1578
    "str x29, [%0, #8] \n\t"
1579
    "str x30, [%0, #16] \n\t"
1580
    "mov x0, sp \n\t"
1581
    "str x0, [%0, #24] \n\t"
1582
    :
1583
    : "r"(&block[0])
1584
    : "memory", "x0"
1585
  );
1586
  startRegs.pc = TaggedUWord(block[0]);
1587
  startRegs.x29 = TaggedUWord(block[1]);
1588
  startRegs.x30 = TaggedUWord(block[2]);
1589
  startRegs.sp = TaggedUWord(block[3]);
1590
  const uintptr_t REDZONE_SIZE = 0;
1591
  uintptr_t start = block[1] - REDZONE_SIZE;
1592
#elif defined(GP_ARCH_mips64)
1593
  volatile uintptr_t block[3];
1594
  MOZ_ASSERT(sizeof(block) == 24);
1595
  __asm__ __volatile__(
1596
    "sd $29, 8(%0)     \n"
1597
    "sd $30, 16(%0)    \n"
1598
    :
1599
    :"r"(block)
1600
    :"memory"
1601
  );
1602
  block[0] = __getpc();
1603
  startRegs.pc = TaggedUWord(block[0]);
1604
  startRegs.sp = TaggedUWord(block[1]);
1605
  startRegs.fp = TaggedUWord(block[2]);
1606
  const uintptr_t REDZONE_SIZE = 0;
1607
  uintptr_t start = block[1] - REDZONE_SIZE;
1608
#else
1609
# error "Unsupported platform"
1610
#endif
1611
1612
0
  // Get hold of the innermost LUL_UNIT_TEST_STACK_SIZE bytes of the
1613
0
  // stack.
1614
0
  uintptr_t end = start + LUL_UNIT_TEST_STACK_SIZE;
1615
0
  uintptr_t ws  = sizeof(void*);
1616
0
  start &= ~(ws-1);
1617
0
  end   &= ~(ws-1);
1618
0
  uintptr_t nToCopy = end - start;
1619
0
  if (nToCopy > lul::N_STACK_BYTES) {
1620
0
    nToCopy = lul::N_STACK_BYTES;
1621
0
  }
1622
0
  MOZ_ASSERT(nToCopy <= lul::N_STACK_BYTES);
1623
0
  StackImage* stackImg = new StackImage();
1624
0
  stackImg->mLen       = nToCopy;
1625
0
  stackImg->mStartAvma = start;
1626
0
  if (nToCopy > 0) {
1627
0
    MOZ_MAKE_MEM_DEFINED((void*)start, nToCopy);
1628
0
    memcpy(&stackImg->mContents[0], (void*)start, nToCopy);
1629
0
  }
1630
0
1631
0
  // Unwind it.
1632
0
  const int MAX_TEST_FRAMES = 64;
1633
0
  uintptr_t framePCs[MAX_TEST_FRAMES];
1634
0
  uintptr_t frameSPs[MAX_TEST_FRAMES];
1635
0
  size_t framesAvail = mozilla::ArrayLength(framePCs);
1636
0
  size_t framesUsed  = 0;
1637
0
  size_t framePointerFramesAcquired = 0;
1638
0
  aLUL->Unwind( &framePCs[0], &frameSPs[0],
1639
0
                &framesUsed, &framePointerFramesAcquired,
1640
0
                framesAvail, &startRegs, stackImg );
1641
0
1642
0
  delete stackImg;
1643
0
1644
0
  //if (0) {
1645
0
  //  // Show what we have.
1646
0
  //  fprintf(stderr, "Got %d frames:\n", (int)framesUsed);
1647
0
  //  for (size_t i = 0; i < framesUsed; i++) {
1648
0
  //    fprintf(stderr, "  [%2d]   SP %p   PC %p\n",
1649
0
  //            (int)i, (void*)frameSPs[i], (void*)framePCs[i]);
1650
0
  //  }
1651
0
  //  fprintf(stderr, "\n");
1652
0
  //}
1653
0
1654
0
  // Check to see if there's a consistent binding between digits in
1655
0
  // the director string ('1' .. '8') and the PC values acquired by
1656
0
  // the unwind.  If there isn't, the unwinding has failed somehow.
1657
0
  uintptr_t binding[8];  // binding for '1' .. binding for '8'
1658
0
  memset((void*)binding, 0, sizeof(binding));
1659
0
1660
0
  // The general plan is to work backwards along the director string
1661
0
  // and forwards along the framePCs array.  Doing so corresponds to
1662
0
  // working outwards from the innermost frame of the recursive test set.
1663
0
  const char* cursor = dstring;
1664
0
1665
0
  // Find the end.  This leaves |cursor| two bytes past the first
1666
0
  // character we want to look at -- see comment below.
1667
0
  while (*cursor) cursor++;
1668
0
1669
0
  // Counts the number of consistent frames.
1670
0
  size_t nConsistent = 0;
1671
0
1672
0
  // Iterate back to the start of the director string.  The starting
1673
0
  // points are a bit complex.  We can't use framePCs[0] because that
1674
0
  // contains the PC in this frame (above).  We can't use framePCs[1]
1675
0
  // because that will contain the PC at return point in the recursive
1676
0
  // test group (TestFn[1-8]) for their call "out" to this function,
1677
0
  // GetAndCheckStackTrace.  Although LUL will compute a correct
1678
0
  // return address, that will not be the same return address as for a
1679
0
  // recursive call out of the the function to another function in the
1680
0
  // group.  Hence we can only start consistency checking at
1681
0
  // framePCs[2].
1682
0
  //
1683
0
  // To be consistent, then, we must ignore the last element in the
1684
0
  // director string as that corresponds to framePCs[1].  Hence the
1685
0
  // start points are: framePCs[2] and the director string 2 bytes
1686
0
  // before the terminating zero.
1687
0
  //
1688
0
  // Also as a result of this, the number of consistent frames counted
1689
0
  // will always be one less than the length of the director string
1690
0
  // (not including its terminating zero).
1691
0
  size_t frameIx;
1692
0
  for (cursor = cursor-2, frameIx = 2;
1693
0
       cursor >= dstring && frameIx < framesUsed;
1694
0
       cursor--, frameIx++) {
1695
0
    char      c  = *cursor;
1696
0
    uintptr_t pc = framePCs[frameIx];
1697
0
    // If this doesn't hold, the director string is ill-formed.
1698
0
    MOZ_ASSERT(c >= '1' && c <= '8');
1699
0
    int n = ((int)c) - ((int)'1');
1700
0
    if (binding[n] == 0) {
1701
0
      // There's no binding for |c| yet, so install |pc| and carry on.
1702
0
      binding[n] = pc;
1703
0
      nConsistent++;
1704
0
      continue;
1705
0
    }
1706
0
    // There's a pre-existing binding for |c|.  Check it's consistent.
1707
0
    if (binding[n] != pc) {
1708
0
      // Not consistent.  Give up now.
1709
0
      break;
1710
0
    }
1711
0
    // Consistent.  Keep going.
1712
0
    nConsistent++;
1713
0
  }
1714
0
1715
0
  // So, did we succeed?
1716
0
  bool passed = nConsistent+1 == strlen(dstring);
1717
0
1718
0
  // Show the results.
1719
0
  char buf[200];
1720
0
  SprintfLiteral(buf, "LULUnitTest:   dstring = %s\n", dstring);
1721
0
  buf[sizeof(buf)-1] = 0;
1722
0
  aLUL->mLog(buf);
1723
0
  SprintfLiteral(buf,
1724
0
                 "LULUnitTest:     %d consistent, %d in dstring: %s\n",
1725
0
                 (int)nConsistent, (int)strlen(dstring),
1726
0
                 passed ? "PASS" : "FAIL");
1727
0
  buf[sizeof(buf)-1] = 0;
1728
0
  aLUL->mLog(buf);
1729
0
1730
0
  return passed;
1731
0
}
1732
1733
1734
// Macro magic to create a set of 8 mutually recursive functions with
1735
// varying frame sizes.  These will recurse amongst themselves as
1736
// specified by |strP|, the directory string, and call
1737
// GetAndCheckStackTrace when the string becomes empty, passing it the
1738
// original value of the string.  This checks the result, printing
1739
// results on |aLUL|'s logging sink, and also returns a boolean
1740
// indicating whether or not the results are acceptable (correct).
1741
1742
#define DECL_TEST_FN(NAME) \
1743
  bool NAME(LUL* aLUL, const char* strPorig, const char* strP);
1744
1745
#define GEN_TEST_FN(NAME, FRAMESIZE) \
1746
0
  bool NAME(LUL* aLUL, const char* strPorig, const char* strP) { \
1747
0
    /* Create a frame of size (at least) FRAMESIZE, so that the */ \
1748
0
    /* 8 functions created by this macro offer some variation in frame */  \
1749
0
    /* sizes.  This isn't as simple as it might seem, since a clever */    \
1750
0
    /* optimizing compiler (eg, clang-5) detects that the array is unused */ \
1751
0
    /* and removes it.  We try to defeat this by passing it to a function */ \
1752
0
    /* in a different compilation unit, and hoping that clang does not */ \
1753
0
    /* notice that the call is a no-op. */ \
1754
0
    char space[FRAMESIZE]; \
1755
0
    Unused << write(1, space, 0); /* write zero bytes of |space| to stdout */ \
1756
0
    \
1757
0
    if (*strP == '\0') { \
1758
0
      /* We've come to the end of the director string. */ \
1759
0
      /* Take a stack snapshot. */ \
1760
0
      return GetAndCheckStackTrace(aLUL, strPorig); \
1761
0
    } else { \
1762
0
      /* Recurse onwards.  This is a bit subtle.  The obvious */ \
1763
0
      /* thing to do here is call onwards directly, from within the */ \
1764
0
      /* arms of the case statement.  That gives a problem in that */ \
1765
0
      /* there will be multiple return points inside each function when */ \
1766
0
      /* unwinding, so it will be difficult to check for consistency */ \
1767
0
      /* against the director string.  Instead, we make an indirect */ \
1768
0
      /* call, so as to guarantee that there is only one call site */ \
1769
0
      /* within each function.  This does assume that the compiler */ \
1770
0
      /* won't transform it back to the simple direct-call form. */ \
1771
0
      /* To discourage it from doing so, the call is bracketed with */ \
1772
0
      /* __asm__ __volatile__ sections so as to make it not-movable. */ \
1773
0
      bool (*nextFn)(LUL*, const char*, const char*) = NULL; \
1774
0
      switch (*strP) { \
1775
0
        case '1': nextFn = TestFn1; break; \
1776
0
        case '2': nextFn = TestFn2; break; \
1777
0
        case '3': nextFn = TestFn3; break; \
1778
0
        case '4': nextFn = TestFn4; break; \
1779
0
        case '5': nextFn = TestFn5; break; \
1780
0
        case '6': nextFn = TestFn6; break; \
1781
0
        case '7': nextFn = TestFn7; break; \
1782
0
        case '8': nextFn = TestFn8; break; \
1783
0
        default:  nextFn = TestFn8; break; \
1784
0
      } \
1785
0
      /* "use" |space| immediately after the recursive call, */ \
1786
0
      /* so as to dissuade clang from deallocating the space while */ \
1787
0
      /* the call is active, or otherwise messing with the stack frame. */ \
1788
0
      __asm__ __volatile__("":::"cc","memory"); \
1789
0
      bool passed = nextFn(aLUL, strPorig, strP+1); \
1790
0
      Unused << write(1, space, 0); \
1791
0
      __asm__ __volatile__("":::"cc","memory"); \
1792
0
      return passed; \
1793
0
    } \
1794
0
  }
Unexecuted instantiation: lul::TestFn1(lul::LUL*, char const*, char const*)
Unexecuted instantiation: lul::TestFn2(lul::LUL*, char const*, char const*)
Unexecuted instantiation: lul::TestFn3(lul::LUL*, char const*, char const*)
Unexecuted instantiation: lul::TestFn4(lul::LUL*, char const*, char const*)
Unexecuted instantiation: lul::TestFn5(lul::LUL*, char const*, char const*)
Unexecuted instantiation: lul::TestFn6(lul::LUL*, char const*, char const*)
Unexecuted instantiation: lul::TestFn7(lul::LUL*, char const*, char const*)
Unexecuted instantiation: lul::TestFn8(lul::LUL*, char const*, char const*)
1795
1796
// The test functions are mutually recursive, so it is necessary to
1797
// declare them before defining them.
1798
DECL_TEST_FN(TestFn1)
1799
DECL_TEST_FN(TestFn2)
1800
DECL_TEST_FN(TestFn3)
1801
DECL_TEST_FN(TestFn4)
1802
DECL_TEST_FN(TestFn5)
1803
DECL_TEST_FN(TestFn6)
1804
DECL_TEST_FN(TestFn7)
1805
DECL_TEST_FN(TestFn8)
1806
1807
GEN_TEST_FN(TestFn1, 123)
1808
GEN_TEST_FN(TestFn2, 456)
1809
GEN_TEST_FN(TestFn3, 789)
1810
GEN_TEST_FN(TestFn4, 23)
1811
GEN_TEST_FN(TestFn5, 47)
1812
GEN_TEST_FN(TestFn6, 117)
1813
GEN_TEST_FN(TestFn7, 1)
1814
GEN_TEST_FN(TestFn8, 99)
1815
1816
1817
// This starts the test sequence going.  Call here to generate a
1818
// sequence of calls as directed by the string |dstring|.  The call
1819
// sequence will, from its innermost frame, finish by calling
1820
// GetAndCheckStackTrace() and passing it |dstring|.
1821
// GetAndCheckStackTrace() will unwind the stack, check consistency
1822
// of those results against |dstring|, and print a pass/fail message
1823
// to aLUL's logging sink.  It also updates the counters in *aNTests
1824
// and aNTestsPassed.
1825
__attribute__((noinline)) void
1826
TestUnw(/*OUT*/int* aNTests, /*OUT*/int*aNTestsPassed,
1827
        LUL* aLUL, const char* dstring)
1828
0
{
1829
0
  // Ensure that the stack has at least this much space on it.  This
1830
0
  // makes it safe to saw off the top LUL_UNIT_TEST_STACK_SIZE bytes
1831
0
  // and hand it to LUL.  Safe in the sense that no segfault can
1832
0
  // happen because the stack is at least this big.  This is all
1833
0
  // somewhat dubious in the sense that a sufficiently clever compiler
1834
0
  // (clang, for one) can figure out that space[] is unused and delete
1835
0
  // it from the frame.  Hence the somewhat elaborate hoop jumping to
1836
0
  // fill it up before the call and to at least appear to use the
1837
0
  // value afterwards.
1838
0
  int i;
1839
0
  volatile char space[LUL_UNIT_TEST_STACK_SIZE];
1840
0
  for (i = 0; i < LUL_UNIT_TEST_STACK_SIZE; i++) {
1841
0
    space[i] = (char)(i & 0x7F);
1842
0
  }
1843
0
1844
0
  // Really run the test.
1845
0
  bool passed = TestFn1(aLUL, dstring, dstring);
1846
0
1847
0
  // Appear to use space[], by visiting the value to compute some kind
1848
0
  // of checksum, and then (apparently) using the checksum.
1849
0
  int sum = 0;
1850
0
  for (i = 0; i < LUL_UNIT_TEST_STACK_SIZE; i++) {
1851
0
    // If this doesn't fool LLVM, I don't know what will.
1852
0
    sum += space[i] - 3*i;
1853
0
  }
1854
0
  __asm__ __volatile__("" : : "r"(sum));
1855
0
1856
0
  // Update the counters.
1857
0
  (*aNTests)++;
1858
0
  if (passed) {
1859
0
    (*aNTestsPassed)++;
1860
0
  }
1861
0
}
1862
1863
1864
void
1865
RunLulUnitTests(/*OUT*/int* aNTests, /*OUT*/int*aNTestsPassed, LUL* aLUL)
1866
0
{
1867
0
  aLUL->mLog(":\n");
1868
0
  aLUL->mLog("LULUnitTest: BEGIN\n");
1869
0
  *aNTests = *aNTestsPassed = 0;
1870
0
  TestUnw(aNTests, aNTestsPassed, aLUL, "11111111");
1871
0
  TestUnw(aNTests, aNTestsPassed, aLUL, "11222211");
1872
0
  TestUnw(aNTests, aNTestsPassed, aLUL, "111222333");
1873
0
  TestUnw(aNTests, aNTestsPassed, aLUL, "1212121231212331212121212121212");
1874
0
  TestUnw(aNTests, aNTestsPassed, aLUL, "31415827271828325332173258");
1875
0
  TestUnw(aNTests, aNTestsPassed, aLUL,
1876
0
          "123456781122334455667788777777777777777777777");
1877
0
  aLUL->mLog("LULUnitTest: END\n");
1878
0
  aLUL->mLog(":\n");
1879
0
}
1880
1881
1882
} // namespace lul