LCOV - code coverage report
Current view: top level - tools/profiler/lul - LulMain.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 582 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 55 0.0 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.13