Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/tools/profiler/lul/LulDwarfSummariser.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 "LulDwarfSummariser.h"
8
9
#include "mozilla/Assertions.h"
10
11
// Set this to 1 for verbose logging
12
0
#define DEBUG_SUMMARISER 0
13
14
namespace lul {
15
16
// Do |s64|'s lowest 32 bits sign extend back to |s64| itself?
17
0
static inline bool fitsIn32Bits(int64 s64) {
18
0
  return s64 == ((s64 & 0xffffffff) ^ 0x80000000) - 0x80000000;
19
0
}
20
21
// Check a LExpr prefix expression, starting at pfxInstrs[start] up to
22
// the next PX_End instruction, to ensure that:
23
// * It only mentions registers that are tracked on this target
24
// * The start point is sane
25
// If the expression is ok, return NULL.  Else return a pointer
26
// a const char* holding a bit of text describing the problem.
27
static const char*
28
checkPfxExpr(const vector<PfxInstr>* pfxInstrs, int64_t start)
29
0
{
30
0
  size_t nInstrs = pfxInstrs->size();
31
0
  if (start < 0 || start >= (ssize_t)nInstrs) {
32
0
    return "bogus start point";
33
0
  }
34
0
  size_t i;
35
0
  for (i = start; i < nInstrs; i++) {
36
0
    PfxInstr pxi = (*pfxInstrs)[i];
37
0
    if (pxi.mOpcode == PX_End)
38
0
      break;
39
0
    if (pxi.mOpcode == PX_DwReg &&
40
0
        !registerIsTracked((DW_REG_NUMBER)pxi.mOperand)) {
41
0
      return "uses untracked reg";
42
0
    }
43
0
  }
44
0
  return nullptr; // success
45
0
}
46
47
48
Summariser::Summariser(SecMap* aSecMap, uintptr_t aTextBias,
49
                       void(*aLog)(const char*))
50
  : mSecMap(aSecMap)
51
  , mTextBias(aTextBias)
52
  , mLog(aLog)
53
0
{
54
0
  mCurrAddr = 0;
55
0
  mMax1Addr = 0; // Gives an empty range.
56
0
57
0
  // Initialise the running RuleSet to "haven't got a clue" status.
58
0
  new (&mCurrRules) RuleSet();
59
0
}
60
61
void
62
Summariser::Entry(uintptr_t aAddress, uintptr_t aLength)
63
0
{
64
0
  aAddress += mTextBias;
65
0
  if (DEBUG_SUMMARISER) {
66
0
    char buf[100];
67
0
    SprintfLiteral(buf,
68
0
                   "LUL Entry(%llx, %llu)\n",
69
0
                   (unsigned long long int)aAddress,
70
0
                   (unsigned long long int)aLength);
71
0
    mLog(buf);
72
0
  }
73
0
  // This throws away any previous summary, that is, assumes
74
0
  // that the previous summary, if any, has been properly finished
75
0
  // by a call to End().
76
0
  mCurrAddr = aAddress;
77
0
  mMax1Addr = aAddress + aLength;
78
0
  new (&mCurrRules) RuleSet();
79
0
}
80
81
void
82
Summariser::Rule(uintptr_t aAddress, int aNewReg,
83
                 LExprHow how, int16_t oldReg, int64_t offset)
84
0
{
85
0
  aAddress += mTextBias;
86
0
  if (DEBUG_SUMMARISER) {
87
0
    char buf[100];
88
0
    if (how == NODEREF || how == DEREF) {
89
0
      bool deref = how == DEREF;
90
0
      SprintfLiteral(buf,
91
0
                     "LUL  0x%llx  old-r%d = %sr%d + %lld%s\n",
92
0
                     (unsigned long long int)aAddress, aNewReg,
93
0
                     deref ? "*(" : "", (int)oldReg, (long long int)offset,
94
0
                     deref ? ")" : "");
95
0
    } else if (how == PFXEXPR) {
96
0
      SprintfLiteral(buf,
97
0
                     "LUL  0x%llx  old-r%d = pfx-expr-at %lld\n",
98
0
                     (unsigned long long int)aAddress, aNewReg,
99
0
                     (long long int)offset);
100
0
    } else {
101
0
      SprintfLiteral(buf,
102
0
                     "LUL  0x%llx  old-r%d = (invalid LExpr!)\n",
103
0
                     (unsigned long long int)aAddress, aNewReg);
104
0
    }
105
0
    mLog(buf);
106
0
  }
107
0
108
0
  if (mCurrAddr < aAddress) {
109
0
    // Flush the existing summary first.
110
0
    mCurrRules.mAddr = mCurrAddr;
111
0
    mCurrRules.mLen  = aAddress - mCurrAddr;
112
0
    mSecMap->AddRuleSet(&mCurrRules);
113
0
    if (DEBUG_SUMMARISER) {
114
0
      mLog("LUL  "); mCurrRules.Print(mLog);
115
0
      mLog("\n");
116
0
    }
117
0
    mCurrAddr = aAddress;
118
0
  }
119
0
120
0
  // If for some reason summarisation fails, either or both of these
121
0
  // become non-null and point at constant text describing the
122
0
  // problem.  Using two rather than just one avoids complications of
123
0
  // having to concatenate two strings to produce a complete error message.
124
0
  const char* reason1 = nullptr;
125
0
  const char* reason2 = nullptr;
126
0
127
0
  // |offset| needs to be a 32 bit value that sign extends to 64 bits
128
0
  // on a 64 bit target.  We will need to incorporate |offset| into
129
0
  // any LExpr made here.  So we may as well check it right now.
130
0
  if (!fitsIn32Bits(offset)) {
131
0
    reason1 = "offset not in signed 32-bit range";
132
0
    goto cant_summarise;
133
0
  }
134
0
135
0
  // FIXME: factor out common parts of the arch-dependent summarisers.
136
0
137
#if defined(GP_ARCH_arm)
138
139
  // ----------------- arm ----------------- //
140
141
  // Now, can we add the rule to our summary?  This depends on whether
142
  // the registers and the overall expression are representable.  This
143
  // is the heart of the summarisation process.
144
  switch (aNewReg) {
145
146
    case DW_REG_CFA:
147
      // This is a rule that defines the CFA.  The only forms we
148
      // choose to represent are: r7/11/12/13 + offset.  The offset
149
      // must fit into 32 bits since 'uintptr_t' is 32 bit on ARM,
150
      // hence there is no need to check it for overflow.
151
      if (how != NODEREF) {
152
        reason1 = "rule for DW_REG_CFA: invalid |how|";
153
        goto cant_summarise;
154
      }
155
      switch (oldReg) {
156
        case DW_REG_ARM_R7:  case DW_REG_ARM_R11:
157
        case DW_REG_ARM_R12: case DW_REG_ARM_R13:
158
          break;
159
        default:
160
          reason1 = "rule for DW_REG_CFA: invalid |oldReg|";
161
          goto cant_summarise;
162
      }
163
      mCurrRules.mCfaExpr = LExpr(how, oldReg, offset);
164
      break;
165
166
    case DW_REG_ARM_R7:  case DW_REG_ARM_R11: case DW_REG_ARM_R12:
167
    case DW_REG_ARM_R13: case DW_REG_ARM_R14: case DW_REG_ARM_R15: {
168
      // This is a new rule for R7, R11, R12, R13 (SP), R14 (LR) or
169
      // R15 (the return address).
170
      switch (how) {
171
        case NODEREF: case DEREF:
172
          // Check the old register is one we're tracking.
173
          if (!registerIsTracked((DW_REG_NUMBER)oldReg) &&
174
              oldReg != DW_REG_CFA) {
175
            reason1 = "rule for R7/11/12/13/14/15: uses untracked reg";
176
            goto cant_summarise;
177
          }
178
          break;
179
        case PFXEXPR: {
180
          // Check that the prefix expression only mentions tracked registers.
181
          const vector<PfxInstr>* pfxInstrs = mSecMap->GetPfxInstrs();
182
          reason2 = checkPfxExpr(pfxInstrs, offset);
183
          if (reason2) {
184
            reason1 = "rule for R7/11/12/13/14/15: ";
185
            goto cant_summarise;
186
          }
187
          break;
188
        }
189
        default:
190
          goto cant_summarise;
191
      }
192
      LExpr expr = LExpr(how, oldReg, offset);
193
      switch (aNewReg) {
194
        case DW_REG_ARM_R7:  mCurrRules.mR7expr  = expr; break;
195
        case DW_REG_ARM_R11: mCurrRules.mR11expr = expr; break;
196
        case DW_REG_ARM_R12: mCurrRules.mR12expr = expr; break;
197
        case DW_REG_ARM_R13: mCurrRules.mR13expr = expr; break;
198
        case DW_REG_ARM_R14: mCurrRules.mR14expr = expr; break;
199
        case DW_REG_ARM_R15: mCurrRules.mR15expr = expr; break;
200
        default: MOZ_ASSERT(0);
201
      }
202
      break;
203
    }
204
205
    default:
206
      // Leave |reason1| and |reason2| unset here.  This program point
207
      // is reached so often that it causes a flood of "Can't
208
      // summarise" messages.  In any case, we don't really care about
209
      // the fact that this summary would produce a new value for a
210
      // register that we're not tracking.  We do on the other hand
211
      // care if the summary's expression *uses* a register that we're
212
      // not tracking.  But in that case one of the above failures
213
      // should tell us which.
214
      goto cant_summarise;
215
  }
216
217
  // Mark callee-saved registers (r4 .. r11) as unchanged, if there is
218
  // no other information about them.  FIXME: do this just once, at
219
  // the point where the ruleset is committed.
220
  if (mCurrRules.mR7expr.mHow == UNKNOWN) {
221
    mCurrRules.mR7expr = LExpr(NODEREF, DW_REG_ARM_R7, 0);
222
  }
223
  if (mCurrRules.mR11expr.mHow == UNKNOWN) {
224
    mCurrRules.mR11expr = LExpr(NODEREF, DW_REG_ARM_R11, 0);
225
  }
226
  if (mCurrRules.mR12expr.mHow == UNKNOWN) {
227
    mCurrRules.mR12expr = LExpr(NODEREF, DW_REG_ARM_R12, 0);
228
  }
229
230
  // The old r13 (SP) value before the call is always the same as the
231
  // CFA.
232
  mCurrRules.mR13expr = LExpr(NODEREF, DW_REG_CFA, 0);
233
234
  // If there's no information about R15 (the return address), say
235
  // it's a copy of R14 (the link register).
236
  if (mCurrRules.mR15expr.mHow == UNKNOWN) {
237
    mCurrRules.mR15expr = LExpr(NODEREF, DW_REG_ARM_R14, 0);
238
  }
239
240
#elif defined(GP_ARCH_arm64)
241
242
  // ----------------- arm64 ----------------- //
243
244
  switch (aNewReg) {
245
    case DW_REG_CFA:
246
      if (how != NODEREF) {
247
        reason1 = "rule for DW_REG_CFA: invalid |how|";
248
        goto cant_summarise;
249
      }
250
      switch (oldReg) {
251
        case DW_REG_AARCH64_X29:
252
        case DW_REG_AARCH64_SP:
253
          break;
254
        default:
255
          reason1 = "rule for DW_REG_CFA: invalid |oldReg|";
256
          goto cant_summarise;
257
      }
258
      mCurrRules.mCfaExpr = LExpr(how, oldReg, offset);
259
      break;
260
261
    case DW_REG_AARCH64_X29:
262
    case DW_REG_AARCH64_X30:
263
    case DW_REG_AARCH64_SP: {
264
      switch (how) {
265
        case NODEREF:
266
        case DEREF:
267
          // Check the old register is one we're tracking.
268
          if (!registerIsTracked((DW_REG_NUMBER)oldReg) &&
269
              oldReg != DW_REG_CFA) {
270
            reason1 = "rule for X29/X30/SP: uses untracked reg";
271
            goto cant_summarise;
272
          }
273
          break;
274
        case PFXEXPR: {
275
          // Check that the prefix expression only mentions tracked registers.
276
          const vector<PfxInstr>* pfxInstrs = mSecMap->GetPfxInstrs();
277
          reason2 = checkPfxExpr(pfxInstrs, offset);
278
          if (reason2) {
279
            reason1 = "rule for X29/X30/SP: ";
280
            goto cant_summarise;
281
          }
282
          break;
283
        }
284
        default:
285
          goto cant_summarise;
286
      }
287
      LExpr expr = LExpr(how, oldReg, offset);
288
      switch (aNewReg) {
289
        case DW_REG_AARCH64_X29: mCurrRules.mX29expr = expr; break;
290
        case DW_REG_AARCH64_X30: mCurrRules.mX30expr = expr; break;
291
        case DW_REG_AARCH64_SP:  mCurrRules.mSPexpr  = expr; break;
292
        default: MOZ_ASSERT(0);
293
      }
294
      break;
295
    }
296
    default:
297
     // Leave |reason1| and |reason2| unset here, for the reasons explained
298
     // in the analogous point
299
     goto cant_summarise;
300
  }
301
302
  if (mCurrRules.mX29expr.mHow == UNKNOWN) {
303
    mCurrRules.mX29expr = LExpr(NODEREF, DW_REG_AARCH64_X29, 0);
304
  }
305
  if (mCurrRules.mX30expr.mHow == UNKNOWN) {
306
    mCurrRules.mX30expr = LExpr(NODEREF, DW_REG_AARCH64_X30, 0);
307
  }
308
  // On aarch64, it seems the old SP value before the call is always the
309
  // same as the CFA.  Therefore, in the absence of any other way to
310
  // recover the SP, specify that the CFA should be copied.
311
  if (mCurrRules.mSPexpr.mHow == UNKNOWN) {
312
    mCurrRules.mSPexpr = LExpr(NODEREF, DW_REG_CFA, 0);
313
  }
314
#elif defined(GP_ARCH_amd64) || defined(GP_ARCH_x86)
315
316
0
  // ---------------- x64/x86 ---------------- //
317
0
318
0
  // Now, can we add the rule to our summary?  This depends on whether
319
0
  // the registers and the overall expression are representable.  This
320
0
  // is the heart of the summarisation process.
321
0
  switch (aNewReg) {
322
0
323
0
    case DW_REG_CFA: {
324
0
      // This is a rule that defines the CFA.  The only forms we choose to
325
0
      // represent are: = SP+offset, = FP+offset, or =prefix-expr.
326
0
      switch (how) {
327
0
        case NODEREF:
328
0
          if (oldReg != DW_REG_INTEL_XSP && oldReg != DW_REG_INTEL_XBP) {
329
0
            reason1 = "rule for DW_REG_CFA: invalid |oldReg|";
330
0
            goto cant_summarise;
331
0
          }
332
0
          break;
333
0
        case DEREF:
334
0
          reason1 = "rule for DW_REG_CFA: invalid |how|";
335
0
          goto cant_summarise;
336
0
        case PFXEXPR: {
337
0
          // Check that the prefix expression only mentions tracked registers.
338
0
          const vector<PfxInstr>* pfxInstrs = mSecMap->GetPfxInstrs();
339
0
          reason2 = checkPfxExpr(pfxInstrs, offset);
340
0
          if (reason2) {
341
0
            reason1 = "rule for CFA: ";
342
0
            goto cant_summarise;
343
0
          }
344
0
          break;
345
0
        }
346
0
        default:
347
0
          goto cant_summarise;
348
0
      }
349
0
      mCurrRules.mCfaExpr = LExpr(how, oldReg, offset);
350
0
      break;
351
0
    }
352
0
353
0
    case DW_REG_INTEL_XSP: case DW_REG_INTEL_XBP: case DW_REG_INTEL_XIP: {
354
0
      // This is a new rule for XSP, XBP or XIP (the return address).
355
0
      switch (how) {
356
0
        case NODEREF: case DEREF:
357
0
          // Check the old register is one we're tracking.
358
0
          if (!registerIsTracked((DW_REG_NUMBER)oldReg) &&
359
0
              oldReg != DW_REG_CFA) {
360
0
            reason1 = "rule for XSP/XBP/XIP: uses untracked reg";
361
0
            goto cant_summarise;
362
0
          }
363
0
          break;
364
0
        case PFXEXPR: {
365
0
          // Check that the prefix expression only mentions tracked registers.
366
0
          const vector<PfxInstr>* pfxInstrs = mSecMap->GetPfxInstrs();
367
0
          reason2 = checkPfxExpr(pfxInstrs, offset);
368
0
          if (reason2) {
369
0
            reason1 = "rule for XSP/XBP/XIP: ";
370
0
            goto cant_summarise;
371
0
          }
372
0
          break;
373
0
        }
374
0
        default:
375
0
          goto cant_summarise;
376
0
      }
377
0
      LExpr expr = LExpr(how, oldReg, offset);
378
0
      switch (aNewReg) {
379
0
        case DW_REG_INTEL_XBP: mCurrRules.mXbpExpr = expr; break;
380
0
        case DW_REG_INTEL_XSP: mCurrRules.mXspExpr = expr; break;
381
0
        case DW_REG_INTEL_XIP: mCurrRules.mXipExpr = expr; break;
382
0
        default: MOZ_CRASH("impossible value for aNewReg");
383
0
      }
384
0
      break;
385
0
    }
386
0
387
0
    default:
388
0
      // Leave |reason1| and |reason2| unset here, for the reasons
389
0
      // explained in the analogous point in the ARM case just above.
390
0
      goto cant_summarise;
391
0
392
0
  }
393
0
394
0
  // On Intel, it seems the old SP value before the call is always the
395
0
  // same as the CFA.  Therefore, in the absence of any other way to
396
0
  // recover the SP, specify that the CFA should be copied.
397
0
  if (mCurrRules.mXspExpr.mHow == UNKNOWN) {
398
0
    mCurrRules.mXspExpr = LExpr(NODEREF, DW_REG_CFA, 0);
399
0
  }
400
0
401
0
  // Also, gcc says "Undef" for BP when it is unchanged.
402
0
  if (mCurrRules.mXbpExpr.mHow == UNKNOWN) {
403
0
    mCurrRules.mXbpExpr = LExpr(NODEREF, DW_REG_INTEL_XBP, 0);
404
0
  }
405
0
406
#elif defined(GP_ARCH_mips64)
407
  // ---------------- mips ---------------- //
408
  //
409
  // Now, can we add the rule to our summary?  This depends on whether
410
  // the registers and the overall expression are representable.  This
411
  // is the heart of the summarisation process.
412
   switch (aNewReg) {
413
414
    case DW_REG_CFA:
415
      // This is a rule that defines the CFA.  The only forms we can
416
      // represent are: = SP+offset or = FP+offset.
417
      if (how != NODEREF) {
418
        reason1 = "rule for DW_REG_CFA: invalid |how|";
419
        goto cant_summarise;
420
      }
421
      if (oldReg != DW_REG_MIPS_SP && oldReg != DW_REG_MIPS_FP) {
422
        reason1 = "rule for DW_REG_CFA: invalid |oldReg|";
423
        goto cant_summarise;
424
      }
425
      mCurrRules.mCfaExpr = LExpr(how, oldReg, offset);
426
      break;
427
428
   case DW_REG_MIPS_SP: case DW_REG_MIPS_FP: case DW_REG_MIPS_PC: {
429
      // This is a new rule for SP, FP or PC (the return address).
430
      switch (how) {
431
        case NODEREF: case DEREF:
432
          // Check the old register is one we're tracking.
433
          if (!registerIsTracked((DW_REG_NUMBER)oldReg) &&
434
              oldReg != DW_REG_CFA) {
435
            reason1 = "rule for SP/FP/PC: uses untracked reg";
436
            goto cant_summarise;
437
          }
438
          break;
439
        case PFXEXPR: {
440
          // Check that the prefix expression only mentions tracked registers.
441
          const vector<PfxInstr>* pfxInstrs = mSecMap->GetPfxInstrs();
442
          reason2 = checkPfxExpr(pfxInstrs, offset);
443
          if (reason2) {
444
            reason1 = "rule for SP/FP/PC: ";
445
            goto cant_summarise;
446
          }
447
          break;
448
        }
449
        default:
450
          goto cant_summarise;
451
      }
452
      LExpr expr = LExpr(how, oldReg, offset);
453
      switch (aNewReg) {
454
        case DW_REG_MIPS_FP: mCurrRules.mFPexpr = expr; break;
455
        case DW_REG_MIPS_SP: mCurrRules.mSPexpr = expr; break;
456
        case DW_REG_MIPS_PC: mCurrRules.mPCexpr = expr; break;
457
        default: MOZ_CRASH("impossible value for aNewReg");
458
      }
459
      break;
460
    }
461
    default:
462
      // Leave |reason1| and |reason2| unset here, for the reasons
463
      // explained in the analogous point in the ARM case just above.
464
      goto cant_summarise;
465
  }
466
467
  // On MIPS, it seems the old SP value before the call is always the
468
  // same as the CFA.  Therefore, in the absence of any other way to
469
  // recover the SP, specify that the CFA should be copied.
470
  if (mCurrRules.mSPexpr.mHow == UNKNOWN) {
471
    mCurrRules.mSPexpr = LExpr(NODEREF, DW_REG_CFA, 0);
472
  }
473
474
  // Also, gcc says "Undef" for FP when it is unchanged.
475
  if (mCurrRules.mFPexpr.mHow == UNKNOWN) {
476
    mCurrRules.mFPexpr = LExpr(NODEREF, DW_REG_MIPS_FP, 0);
477
  }
478
479
#else
480
481
# error "Unsupported arch"
482
#endif
483
484
0
  return;
485
0
486
0
 cant_summarise:
487
0
  if (reason1 || reason2) {
488
0
    char buf[200];
489
0
    SprintfLiteral(buf, "LUL  can't summarise: "
490
0
                        "SVMA=0x%llx: %s%s, expr=LExpr(%s,%u,%lld)\n",
491
0
                   (unsigned long long int)(aAddress - mTextBias),
492
0
                   reason1 ? reason1 : "", reason2 ? reason2 : "",
493
0
                   NameOf_LExprHow(how),
494
0
                   (unsigned int)oldReg, (long long int)offset);
495
0
    mLog(buf);
496
0
  }
497
0
}
498
499
uint32_t
500
Summariser::AddPfxInstr(PfxInstr pfxi)
501
0
{
502
0
  return mSecMap->AddPfxInstr(pfxi);
503
0
}
504
505
void
506
Summariser::End()
507
0
{
508
0
  if (DEBUG_SUMMARISER) {
509
0
    mLog("LUL End\n");
510
0
  }
511
0
  if (mCurrAddr < mMax1Addr) {
512
0
    mCurrRules.mAddr = mCurrAddr;
513
0
    mCurrRules.mLen  = mMax1Addr - mCurrAddr;
514
0
    mSecMap->AddRuleSet(&mCurrRules);
515
0
    if (DEBUG_SUMMARISER) {
516
0
      mLog("LUL  "); mCurrRules.Print(mLog);
517
0
      mLog("\n");
518
0
    }
519
0
  }
520
0
}
521
522
} // namespace lul