(file) Return to Frame.cpp CVS log (file) (dir) Up to [OMI] / omi / nits / base

  1 krisbash 1.1 #include "config.h"
  2              
  3              #if defined(CONFIG_HAVE_BACKTRACE)
  4              
  5              #define PSAPI_VERSION 2
  6              
  7              #ifdef _MSC_VER
  8                  #include <windows.h>
  9                  #include <psapi.h>
 10                  #include <strsafe.h>
 11              #else
 12                  #include <execinfo.h>
 13                  #include <dlfcn.h>
 14                  #include <sys/types.h>
 15                  #include <sys/stat.h>
 16                  #include <unistd.h>
 17              #endif
 18              
 19              #include "Globals.h"
 20              #include "Frame.h"
 21              #include <pal/strings.h>
 22 krisbash 1.1 
 23              using namespace TestSystem;
 24              
 25              LocalModule FrameCache::s_modules[FrameCache::ModuleCount];
 26              size_t FrameCache::s_hash[HashSize];
 27              
 28              void FrameCache::Reset()
 29              {
 30              	m_spinlock = 0;
 31                  m_dataUsed = 0;
 32                  m_modulesUsed = 0;
 33                  m_addressUsed = 0;
 34                  m_startMask = 0;
 35              }
 36              
 37              bool FrameCache::ShouldFault(ptrdiff_t limit, ptrdiff_t attempt, _Out_ bool &filtered)
 38              {
 39                  Atomic_Lock(&m_spinlock);
 40              
 41                  //unsigned __int64 start = ReadTimeStampCounter();
 42                  int count = 0;
 43 krisbash 1.1     void **stackFrames = NULL;
 44              #ifdef _MSC_VER
 45                  void *actualStackFrames[DesiredFrames];
 46                  count = CaptureStackBackTrace(SkippedFrames, DesiredFrames, actualStackFrames, NULL);
 47                  stackFrames = actualStackFrames;
 48              #else
 49                  // backtrace does not let you specify skippedFrames, so here I am grabbing (SkippedFrames + DesiredFrames) 
 50                  // and then skipping the unneeded frames myself to have same behaviour both on windows and non-windows 
 51                  void *actualStackFrames[SkippedFrames + DesiredFrames];
 52                  count = backtrace(actualStackFrames, DesiredFrames + SkippedFrames);
 53                  stackFrames = actualStackFrames + SkippedFrames;
 54                  count = count - SkippedFrames;
 55              #endif
 56              
 57                  bool shouldFault = false;
 58                  filtered = false;
 59              
 60                  GetGlobals().GetStats().stackLookups++;
 61                  GetGlobals().GetStats().frameLookups += count;
 62              
 63                  //Process-local frame hash (avoids translation).
 64 krisbash 1.1     int newCount = 0;
 65                  for (int i = 0; i < count; i++)
 66                  {
 67                      size_t address = reinterpret_cast<size_t>(stackFrames[i]);
 68                      int hash = static_cast<int>(address & HashMask);
 69                      if (s_hash[hash] != address)
 70                      {
 71                          //Don't update the table yet.
 72                          //We may or may not actually fault this location.
 73                          stackFrames[newCount++] = reinterpret_cast<void *>(address);
 74                      }
 75                  }
 76                  GetGlobals().GetStats().frameHashHits += (count - newCount);
 77                  count = newCount;
 78              
 79                  for (int i = 0; i < count; i++)
 80                  {
 81                      size_t address = reinterpret_cast<size_t>(stackFrames[i]);
 82                      int hash = static_cast<int>(address & HashMask);
 83                      unsigned key = TranslateAddress(stackFrames[i]);
 84                      if (!FindRecord(key))
 85 krisbash 1.1         {
 86                          //If the attempt number is less than the previous one,
 87                          //  then this could be orphaned code.
 88                          if (attempt >= limit)
 89                          {
 90                              //Make sure the process-local hash table is updated.
 91                              s_hash[hash] = address;
 92                              shouldFault = true;
 93                              InsertRecord(key);
 94                          }
 95                          else
 96                          {
 97                              filtered = true;
 98                          }
 99                      }
100                      else
101                      {
102                          //Make sure the process-local hash table is updated.
103                          s_hash[hash] = address;
104                      }
105                  }
106 krisbash 1.1 
107                  //unsigned __int64 stop = ReadTimeStampCounter();
108                  //GetGlobals().GetStats().stackTicks += static_cast<unsigned>(stop - start);
109                  Atomic_Unlock(&m_spinlock);
110                  return shouldFault;
111              }
112              
113              unsigned FrameCache::TranslateAddress(_In_ void *pointer)
114              {
115                  size_t address = reinterpret_cast<size_t>(pointer);
116                  unsigned i;
117              
118                  //First check already known address ranges.
119                  for (i = 0; i < m_modulesUsed; i++)
120                  {
121                      LocalModule &local = s_modules[i];
122                      if (address >= local.base && address < local.end)
123                      {
124                          break;
125                      }
126                  }
127 krisbash 1.1 
128                  if (i == m_modulesUsed)
129                  {
130                  	char buffer[MAX_PATH] = "<unknown>";
131                  	DWORD sizeOfImage = 0;
132                  	void *baseOfDll = 0;
133              #ifdef _MSC_VER
134                      //Determine which module name this address belongs to.
135                      HMODULE handle;
136                      MODULEINFO info;
137              
138                      if (!GetModuleHandleEx(
139                                  GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS |
140                                  GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
141                                  reinterpret_cast<LPCTSTR>(address),
142                                  &handle))
143                      {
144                          return 0;
145                      }
146              
147                      GetModuleFileNameA(handle, buffer, sizeof(buffer));
148 krisbash 1.1         K32GetModuleInformation(GetCurrentProcess(), handle, &info, sizeof(info));
149                      sizeOfImage = info.SizeOfImage;
150                      baseOfDll = info.lpBaseOfDll;
151              #else
152                      Dl_info myDlInfo;        
153                      if(!dladdr(pointer, &myDlInfo))
154                      {
155                      	return 0;
156                      }
157                      Strlcpy(buffer, myDlInfo.dli_fname, MAX_PATH);
158                      baseOfDll = myDlInfo.dli_fbase;
159                      struct stat dli_fname_stat;
160                      if(stat(buffer, &dli_fname_stat) != 0)
161                      {
162                      	return 0;
163                      }
164                      sizeOfImage = (DWORD)dli_fname_stat.st_size;
165              
166              #endif
167                      i = FindModule(buffer, sizeOfImage);
168                      LocalModule &local = s_modules[i];
169 krisbash 1.1         local.base = reinterpret_cast<size_t>(baseOfDll);
170              
171              #ifdef _MSC_VER
172                      local.end = local.base + sizeOfImage;
173              #else
174                      // on non-windows we have not found a way to get exact range of library loaded addresses in memory so it is better to  
175                      // keep updating the local.end as we know an address at a time. Also the current assumption here is that if a shared lib is loaded between addresses x to x + y
176                      // then there is no other shared library loaded in between (x, x+y) range; if this does not hold good; then we could be in trouble with the windows based approach of having local.base and local.end
177                      if(local.end < address)
178                      {
179                          local.end = address;
180                      }
181              #endif
182                  }
183              
184                  LocalModule &local = s_modules[i];
185                  GlobalModule &module = m_modules[i];
186                  size_t diff = address - local.base;
187                  unsigned offset = static_cast<unsigned>(diff);
188                  return offset + module.base;
189              }
190 krisbash 1.1 
191              unsigned FrameCache::FindModule(_In_z_ char *name, unsigned size)
192              {
193                  for (unsigned i = 0; i < m_modulesUsed; i++)
194                  {
195                      //N.B. This comparison must be case-insensitive.
196                      //  GetModuleFileName returns names with different cases.
197                      GlobalModule &module = m_modules[i];
198                      if (Strcasecmp(name, module.name) == 0 && size == module.size)
199                      {
200                          return i;
201                      }
202                  }
203              
204                  if (m_modulesUsed == ModuleCount)
205                  {
206                      throw Exception();
207                  }
208              
209                  //Not found, so create a new GlobalModule.
210                  unsigned i = m_modulesUsed++;
211 krisbash 1.1     GlobalModule &module = m_modules[i];
212                  Strlcpy(module.name, name, MAX_PATH);
213              
214                  module.base = m_addressUsed;
215                  module.size = size;
216                  m_addressUsed += size;
217                  return i;
218              }
219              
220              bool FrameCache::FindRecord(unsigned key)
221              {
222                  unsigned level = 0;
223              
224                  //The cache has no record in slot 0.
225                  //Bit i (0-15) in m_dataUsed indicates that slots [2^i, 2*2^i) are in use.
226                  unsigned sections = m_dataUsed;
227                  for (unsigned mask = m_startMask; sections != 0; mask >>= 1)
228                  {
229                      if ((sections & mask) == 0)
230                      {
231                          continue;
232 krisbash 1.1         }
233              
234                      sections &= ~mask;
235                      if (BinarySearch(key, mask, mask + mask))
236                      {
237                          GetGlobals().GetStats().frameHits[level]++;
238                          return true;
239                      }
240              
241                      level++;
242                  }
243              
244                  return false;
245              }
246              
247              bool FrameCache::BinarySearch(unsigned key, int low, int high)
248              {
249                  while (low < high - 1)
250                  {
251                      int middle = (low + high) >> 1;
252              #ifdef _MSC_VER
253 krisbash 1.1 #pragma prefast(push)
254              #pragma prefast(disable:26001)
255              #endif
256                      if (key >= m_data[middle].key)
257              #ifdef _MSC_VER
258              #pragma prefast(pop)
259              #endif
260                      {
261                          low = middle;
262                      }
263                      else
264                      {
265                          high = middle;
266                      }
267                  }
268              
269                  //Four possible outcomes:
270                  //1) key > everything. low == originalHigh - 1.
271                  //2) key < everything. high == originalLow + 1.
272                  //3) key is between two nodes.
273                  //4) key matches data[low].
274 krisbash 1.1     //
275                  //Total number of comparisons is 1 + log N.
276                  return key == m_data[low].key;
277              }
278              
279              void FrameCache::InsertRecord(unsigned key)
280              {
281                  GetGlobals().GetStats().frameInsertions++;
282              
283                  //Bump the count.
284                  //This determines which sections will be merged.
285                  if (m_dataUsed == CacheSize - 1)
286                  {
287                      //The cache is full.
288                      //Discard the bottom half completely.
289                      m_dataUsed = CacheLastSection;
290                  }
291              
292                  unsigned oldCount = m_dataUsed++;
293              
294                  //Determine the section to merge into.
295 krisbash 1.1     int low = m_dataUsed & ~oldCount;
296              
297                  if (static_cast<unsigned>(low) > m_startMask)
298                  {
299                      m_startMask = low;
300                  }
301              
302                  //Insert the record in the first slot in the merged range.
303                  m_data[low].key = key;
304              
305                  for (int src = 1; src != low; src += src)
306                  {
307                      MergeRecords(low, src, src); 
308                  }
309              }
310              
311              void FrameCache::MergeRecords(int low, int src, int size)
312              {
313                  //Merges [low, low+size) and [src, src+size) into [low, low+2*size).
314                  //Always copy from high to low.
315                  int cursor1 = low + size - 1;
316 krisbash 1.1     int cursor2 = src + size - 1;
317                  for (int dest = low + size + size - 1; dest >= low; dest--)
318                  {
319                      if (cursor2 < src)
320                      {
321                          //Optimization. The remaining items are already in place!
322                          return;
323                      }
324              
325                      if (cursor1 < low ||
326                          m_data[cursor1].key <= m_data[cursor2].key)
327                      {
328                          m_data[dest] = m_data[cursor2--];
329                      }
330                      else
331                      {
332                          m_data[dest] = m_data[cursor1--];
333                      }
334                  }
335              }
336              #endif

ViewCVS 0.9.2