PageRenderTime 72ms CodeModel.GetById 8ms app.highlight 59ms RepoModel.GetById 2ms app.codeStats 0ms

/core/externals/update-engine/externals/google-toolbox-for-mac/Foundation/GTMStackTrace.m

http://macfuse.googlecode.com/
Objective C | 386 lines | 285 code | 39 blank | 62 comment | 58 complexity | 749fe5b5b59c01726e67c37bced77ae3 MD5 | raw file
  1//
  2//  GTMStackTrace.m
  3//
  4//  Copyright 2007-2008 Google Inc.
  5//
  6//  Licensed under the Apache License, Version 2.0 (the "License"); you may not
  7//  use this file except in compliance with the License.  You may obtain a copy
  8//  of the License at
  9//
 10//  http://www.apache.org/licenses/LICENSE-2.0
 11//
 12//  Unless required by applicable law or agreed to in writing, software
 13//  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 14//  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
 15//  License for the specific language governing permissions and limitations under
 16//  the License.
 17//
 18
 19#include <stdlib.h>
 20#include <dlfcn.h>
 21#include <mach-o/nlist.h>
 22#include "GTMStackTrace.h"
 23#include "GTMObjC2Runtime.h"
 24
 25struct GTMClassDescription {
 26  const char *class_name;
 27  Method *class_methods;
 28  unsigned int class_method_count;
 29  Method *instance_methods;
 30  unsigned int instance_method_count;
 31};
 32
 33#pragma mark Private utility functions
 34
 35static struct GTMClassDescription *GTMClassDescriptions(NSUInteger *total_count) {
 36  int class_count = objc_getClassList(nil, 0);
 37  struct GTMClassDescription *class_descs
 38    = calloc(class_count, sizeof(struct GTMClassDescription));
 39  if (class_descs) {
 40    Class *classes = calloc(class_count, sizeof(Class));
 41    if (classes) {
 42      objc_getClassList(classes, class_count);
 43      for (int i = 0; i < class_count; ++i) {
 44        class_descs[i].class_methods
 45          = class_copyMethodList(object_getClass(classes[i]),
 46                                 &class_descs[i].class_method_count);
 47        class_descs[i].instance_methods
 48          = class_copyMethodList(classes[i],
 49                                 &class_descs[i].instance_method_count);
 50        class_descs[i].class_name = class_getName(classes[i]);
 51      }
 52      free(classes);
 53    } else {
 54      // COV_NF_START - Don't know how to force this in a unittest
 55      free(class_descs);
 56      class_descs = NULL;
 57      class_count = 0;
 58      // COV_NF_END
 59    }
 60  }
 61  if (total_count) {
 62    *total_count = class_count;
 63  }
 64  return class_descs;
 65}
 66
 67static void GTMFreeClassDescriptions(struct GTMClassDescription *class_descs,
 68                                     NSUInteger count) {
 69  if (!class_descs) return;
 70  for (NSUInteger i = 0; i < count; ++i) {
 71    if (class_descs[i].instance_methods) {
 72      free(class_descs[i].instance_methods);
 73    }
 74    if (class_descs[i].class_methods) {
 75      free(class_descs[i].class_methods);
 76    }
 77  }
 78  free(class_descs);
 79}
 80
 81static NSUInteger GTMGetStackAddressDescriptorsForAddresses(void *pcs[],
 82                                                            struct GTMAddressDescriptor outDescs[],
 83                                                            NSUInteger count) {
 84  if (count < 1 || !pcs || !outDescs) return 0;
 85
 86  NSUInteger class_desc_count;
 87
 88  // Get our obj-c class descriptions. This is expensive, so we do it once
 89  // at the top. We go through this because dladdr doesn't work with
 90  // obj methods.
 91  struct GTMClassDescription *class_descs
 92    = GTMClassDescriptions(&class_desc_count);
 93  if (class_descs == NULL) {
 94    class_desc_count = 0;
 95  }
 96
 97  // Iterate through the stack.
 98  for (NSUInteger i = 0; i < count; ++i) {
 99    const char *class_name = NULL;
100    BOOL is_class_method = NO;
101    size_t smallest_diff = SIZE_MAX;
102    struct GTMAddressDescriptor *currDesc = &outDescs[i];
103    currDesc->address = pcs[i];
104    Method best_method = NULL;
105    // Iterate through all the classes we know of.
106    for (NSUInteger j = 0; j < class_desc_count; ++j) {
107      // First check the class methods.
108      for (NSUInteger k = 0; k < class_descs[j].class_method_count; ++k) {
109        void *imp = (void *)method_getImplementation(class_descs[j].class_methods[k]);
110        if (imp <= currDesc->address) {
111          size_t diff = (size_t)currDesc->address - (size_t)imp;
112          if (diff < smallest_diff) {
113            best_method = class_descs[j].class_methods[k];
114            class_name = class_descs[j].class_name;
115            is_class_method = YES;
116            smallest_diff = diff;
117          }
118        }
119      }
120      // Then check the instance methods.
121      for (NSUInteger k = 0; k < class_descs[j].instance_method_count; ++k) {
122        void *imp = (void *)method_getImplementation(class_descs[j].instance_methods[k]);
123        if (imp <= currDesc->address) {
124          size_t diff = (size_t)currDesc->address - (size_t)imp;
125          if (diff < smallest_diff) {
126            best_method = class_descs[j].instance_methods[k];
127            class_name = class_descs[j].class_name;
128            is_class_method = NO;
129            smallest_diff = diff;
130          }
131        }
132      }
133    }
134
135    // If we have one, store it off.
136    if (best_method) {
137      currDesc->symbol = sel_getName(method_getName(best_method));
138      currDesc->is_class_method = is_class_method;
139      currDesc->class_name = class_name;
140    }
141    Dl_info info = { NULL, NULL, NULL, NULL };
142
143    // Check to see if the one returned by dladdr is better.
144    dladdr(currDesc->address, &info);
145    if ((size_t)currDesc->address - (size_t)info.dli_saddr < smallest_diff) {
146      currDesc->symbol = info.dli_sname;
147      currDesc->is_class_method = NO;
148      currDesc->class_name = NULL;
149    }
150    currDesc->filename = info.dli_fname;
151    if (!currDesc->symbol) {
152      currDesc->symbol = "???";
153      currDesc->is_class_method = NO;
154      currDesc->class_name = NULL;
155    }
156  }
157  GTMFreeClassDescriptions(class_descs, class_desc_count);
158  return count;
159}
160
161static NSString *GTMStackTraceFromAddressDescriptors(struct GTMAddressDescriptor descs[],
162                                                     NSUInteger count) {
163  NSMutableString *trace = [NSMutableString string];
164
165  for (NSUInteger i = 0; i < count; i++) {
166    // Newline between all the lines
167    if (i) {
168      [trace appendString:@"\n"];
169    }
170    NSString *fileName = nil;
171    if (descs[i].filename) {
172      fileName = [NSString stringWithCString:descs[i].filename
173                                    encoding:NSUTF8StringEncoding];
174      fileName = [fileName lastPathComponent];
175    } else {
176      fileName = @"??";
177    }
178    if (descs[i].class_name) {
179      [trace appendFormat:@"#%-2lu %-35s %#0*lX %s[%s %s]",
180       (unsigned long)i,
181       [fileName UTF8String],
182       // sizeof(void*) * 2 is the length of the hex address (32 vs 64) and + 2
183       // for the 0x prefix
184       (int)(sizeof(void *) * 2 + 2),
185       (unsigned long)descs[i].address,
186       (descs[i].is_class_method ? "+" : "-"),
187       descs[i].class_name,
188       (descs[i].symbol ? descs[i].symbol : "??")];
189    } else {
190      [trace appendFormat:@"#%-2lu %-35s %#0*lX %s()",
191       (unsigned long)i,
192       [fileName UTF8String],
193       // sizeof(void*) * 2 is the length of the hex address (32 vs 64) and + 2
194       // for the 0x prefix
195       (int)(sizeof(void *) * 2 + 2),
196       (unsigned long)descs[i].address,
197       (descs[i].symbol ? descs[i].symbol : "??")];
198    }
199  }
200  return trace;
201}
202
203#pragma mark Public functions
204
205#if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5
206// Before 10.5, we have to do this ourselves.  10.5 adds
207// +[NSThread callStackReturnAddresses].
208
209// Structure representing a small portion of a stack, starting from the saved
210// frame pointer, and continuing through the saved program counter.
211struct GTMStackFrame {
212  void *saved_fp;
213#if defined (__ppc__) || defined(__ppc64__)
214  void *padding;
215#endif
216  void *saved_pc;
217};
218
219// __builtin_frame_address(0) is a gcc builtin that returns a pointer to the
220// current frame pointer.  We then use the frame pointer to walk the stack
221// picking off program counters and other saved frame pointers.  This works
222// great on i386, but PPC requires a little more work because the PC (or link
223// register) isn't always stored on the stack.
224//
225NSUInteger GTMGetStackProgramCounters(void *outPcs[], NSUInteger count) {
226  if (!outPcs || (count < 1)) return 0;
227
228  struct GTMStackFrame *fp;
229#if defined (__ppc__) || defined(__ppc64__)
230  outPcs[0] = __builtin_return_address(0);
231  fp = (struct GTMStackFrame *)__builtin_frame_address(1);
232#elif defined (__i386__) || defined(__x86_64__)
233  fp = (struct GTMStackFrame *)__builtin_frame_address(0);
234#else
235#error architecture not supported
236#endif
237
238  NSUInteger level = 0;
239  while (level < count) {
240    if (fp == NULL) {
241      level--;
242      break;
243    }
244    outPcs[level] = fp->saved_pc;
245    level++;
246    fp = (struct GTMStackFrame *)fp->saved_fp;
247  }
248
249  return level;
250}
251#endif  // MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5
252
253NSUInteger GTMGetStackAddressDescriptors(struct GTMAddressDescriptor outDescs[],
254                                         NSUInteger count) {
255  if (count < 1 || !outDescs) return 0;
256  NSUInteger result = 0;
257
258#if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5
259  // Before 10.5, we collect the stack ourselves.
260
261  void **pcs = calloc(count, sizeof(void*));
262  if (!pcs) return 0;
263
264  NSUInteger newSize = GTMGetStackProgramCounters(pcs, count);
265
266  result = GTMGetStackAddressDescriptorsForAddresses(pcs, outDescs, newSize);
267  free(pcs);
268
269#else  //  MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5
270  // Use +[NSThread callStackReturnAddresses]
271
272  NSArray *addresses = [NSThread callStackReturnAddresses];
273  NSUInteger addrCount = [addresses count];
274  if (addrCount) {
275    void **pcs = calloc(addrCount, sizeof(void*));
276    if (pcs) {
277      void **pcsScanner = pcs;
278      for (NSNumber *address in addresses) {
279        NSUInteger addr = [address unsignedIntegerValue];
280        *pcsScanner = (void *)addr;
281        ++pcsScanner;
282      }
283      if (count < addrCount) {
284        addrCount = count;
285      }
286      // Fill in the desc structures
287      result = GTMGetStackAddressDescriptorsForAddresses(pcs, outDescs, addrCount);
288    }
289    free(pcs);
290  }
291#endif  // MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5
292
293  return result;
294}
295
296NSString *GTMStackTrace(void) {
297  // If we don't have enough frames, return an empty string
298  NSString *result = @"";
299
300#if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5
301  // Before 10.5, we collect the stack ourselves.
302
303  // The maximum number of stack frames that we will walk.  We limit this so
304  // that super-duper recursive functions (or bugs) don't send us for an
305  // infinite loop.
306  struct GTMAddressDescriptor descs[100];
307  size_t depth = sizeof(descs) / sizeof(struct GTMAddressDescriptor);
308  depth = GTMGetStackAddressDescriptors(descs, depth);
309
310  // Start at the second item so that GTMStackTrace and it's utility calls (of
311  // which there is currently 1) is not included in the output.
312  const size_t kTracesToStrip = 2;
313  if (depth > kTracesToStrip) {
314    result = GTMStackTraceFromAddressDescriptors(&descs[kTracesToStrip],
315                                                 (depth - kTracesToStrip));
316  }
317#else  //  MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5
318  // Use +[NSThread callStackReturnAddresses]
319
320  NSArray *addresses = [NSThread callStackReturnAddresses];
321  NSUInteger count = [addresses count];
322  if (count) {
323    void **pcs = calloc(count, sizeof(void*));
324    struct GTMAddressDescriptor *descs
325      = calloc(count, sizeof(struct GTMAddressDescriptor));
326    if (pcs && descs) {
327      void **pcsScanner = pcs;
328      for (NSNumber *address in addresses) {
329        NSUInteger addr = [address unsignedIntegerValue];
330        *pcsScanner = (void *)addr;
331        ++pcsScanner;
332      }
333      // Fill in the desc structures
334      count = GTMGetStackAddressDescriptorsForAddresses(pcs, descs, count);
335      // Build the trace
336      // We skip 1 frame because the +[NSThread callStackReturnAddresses] will
337      // start w/ this frame.
338      const size_t kTracesToStrip = 1;
339      if (count > kTracesToStrip) {
340        result = GTMStackTraceFromAddressDescriptors(&descs[kTracesToStrip],
341                                                     (count - kTracesToStrip));
342      }
343    }
344    free(pcs);
345    free(descs);
346  }
347#endif  // MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5
348
349  return result;
350}
351
352#if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 || \
353    (defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && \
354     (__IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_2_0))
355
356NSString *GTMStackTraceFromException(NSException *e) {
357  NSString *trace = @"";
358
359  // collect the addresses
360  NSArray *addresses = [e callStackReturnAddresses];
361  NSUInteger count = [addresses count];
362  if (count) {
363    void **pcs = calloc(count, sizeof(void*));
364    struct GTMAddressDescriptor *descs
365      = calloc(count, sizeof(struct GTMAddressDescriptor));
366    if (pcs && descs) {
367      void **pcsScanner = pcs;
368      for (NSNumber *address in addresses) {
369        NSUInteger addr = [address unsignedIntegerValue];
370        *pcsScanner = (void *)addr;
371        ++pcsScanner;
372      }
373      // Fill in the desc structures
374      count = GTMGetStackAddressDescriptorsForAddresses(pcs, descs, count);
375      // Build the trace
376      trace = GTMStackTraceFromAddressDescriptors(descs, count);
377    }
378    free(pcs);
379    free(descs);
380  }
381
382  return trace;
383}
384
385#endif  // MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5
386        //__IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_2_0