• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright (c) 2006, Google Inc.
2// All rights reserved.
3//
4// Redistribution and use in source and binary forms, with or without
5// modification, are permitted provided that the following conditions are
6// met:
7//
8//     * Redistributions of source code must retain the above copyright
9// notice, this list of conditions and the following disclaimer.
10//     * Redistributions in binary form must reproduce the above
11// copyright notice, this list of conditions and the following disclaimer
12// in the documentation and/or other materials provided with the
13// distribution.
14//     * Neither the name of Google Inc. nor the names of its
15// contributors may be used to endorse or promote products derived from
16// this software without specific prior written permission.
17//
18// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
30#import <Foundation/Foundation.h>
31#include <sys/stat.h>
32#include <map>
33#include <string>
34#include <iostream>
35#include <fstream>
36#include <utility>
37
38#include "google_breakpad/processor/basic_source_line_resolver.h"
39#include "google_breakpad/processor/minidump.h"
40#include "google_breakpad/processor/system_info.h"
41#include "processor/pathname_stripper.h"
42
43#include "on_demand_symbol_supplier.h"
44#include "common/mac/dump_syms.h"
45
46using std::map;
47using std::string;
48
49using google_breakpad::OnDemandSymbolSupplier;
50using google_breakpad::PathnameStripper;
51using google_breakpad::SymbolSupplier;
52using google_breakpad::SystemInfo;
53
54OnDemandSymbolSupplier::OnDemandSymbolSupplier(const string &search_dir,
55                                               const string &symbol_search_dir)
56  : search_dir_(search_dir) {
57  NSFileManager *mgr = [NSFileManager defaultManager];
58  size_t length = symbol_search_dir.length();
59  if (length) {
60    // Load all sym files in symbol_search_dir into our module_file_map
61    // A symbol file always starts with a line like this:
62    // MODULE mac x86 BBF0A8F9BEADDD2048E6464001CA193F0 GoogleDesktopDaemon
63    // or
64    // MODULE mac ppc BBF0A8F9BEADDD2048E6464001CA193F0 GoogleDesktopDaemon
65    const char *symbolSearchStr = symbol_search_dir.c_str();
66    NSString *symbolSearchPath =
67      [mgr stringWithFileSystemRepresentation:symbolSearchStr
68                                       length:strlen(symbolSearchStr)];
69    NSDirectoryEnumerator *dirEnum = [mgr enumeratorAtPath:symbolSearchPath];
70    NSString *fileName;
71    NSCharacterSet *hexSet =
72      [NSCharacterSet characterSetWithCharactersInString:@"0123456789ABCDEF"];
73    NSCharacterSet *newlineSet =
74      [NSCharacterSet characterSetWithCharactersInString:@"\r\n"];
75    while ((fileName = [dirEnum nextObject])) {
76      // Check to see what type of file we have
77      NSDictionary *attrib = [dirEnum fileAttributes];
78      NSString *fileType = [attrib objectForKey:NSFileType];
79      if ([fileType isEqualToString:NSFileTypeDirectory]) {
80        // Skip subdirectories
81        [dirEnum skipDescendents];
82      } else {
83        NSString *filePath = [symbolSearchPath stringByAppendingPathComponent:fileName];
84        NSString *dataStr = [NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:NULL];
85        if (dataStr) {
86          // Check file to see if it is of appropriate type, and grab module
87          // name.
88          NSScanner *scanner = [NSScanner scannerWithString:dataStr];
89          BOOL goodScan = [scanner scanString:@"MODULE mac " intoString:nil];
90          if (goodScan) {
91            goodScan = ([scanner scanString:@"x86 " intoString:nil] ||
92                        [scanner scanString:@"x86_64 " intoString:nil] ||
93                        [scanner scanString:@"ppc " intoString:nil]);
94            if (goodScan) {
95              NSString *moduleID;
96              goodScan = [scanner scanCharactersFromSet:hexSet
97                                             intoString:&moduleID];
98              if (goodScan) {
99                // Module IDs are always 33 chars long
100                goodScan = [moduleID length] == 33;
101                if (goodScan) {
102                  NSString *moduleName;
103                  goodScan = [scanner scanUpToCharactersFromSet:newlineSet
104                                                     intoString:&moduleName];
105                  if (goodScan) {
106                    goodScan = [moduleName length] > 0;
107                    if (goodScan) {
108                      const char *moduleNameStr = [moduleName UTF8String];
109                      const char *filePathStr = [filePath fileSystemRepresentation];
110                      // Map our file
111                      module_file_map_[moduleNameStr] = filePathStr;
112                    }
113                  }
114                }
115              }
116            }
117          }
118        }
119      }
120    }
121  }
122}
123
124SymbolSupplier::SymbolResult
125OnDemandSymbolSupplier::GetSymbolFile(const CodeModule *module,
126                                      const SystemInfo *system_info,
127                                      string *symbol_file) {
128  string path(GetModuleSymbolFile(module));
129
130  if (path.empty()) {
131    if (!GenerateSymbolFile(module, system_info))
132      return NOT_FOUND;
133
134    path = GetModuleSymbolFile(module);
135  }
136
137  if (path.empty())
138    return NOT_FOUND;
139
140  *symbol_file = path;
141  return FOUND;
142}
143
144SymbolSupplier::SymbolResult
145OnDemandSymbolSupplier::GetSymbolFile(const CodeModule *module,
146                                      const SystemInfo *system_info,
147                                      string *symbol_file,
148                                      string *symbol_data) {
149  SymbolSupplier::SymbolResult s = GetSymbolFile(module,
150                                                 system_info,
151                                                 symbol_file);
152
153
154  if (s == FOUND) {
155    std::ifstream in(symbol_file->c_str());
156    getline(in, *symbol_data, std::string::traits_type::to_char_type(
157                std::string::traits_type::eof()));
158    in.close();
159  }
160
161  return s;
162}
163
164SymbolSupplier::SymbolResult
165OnDemandSymbolSupplier::GetCStringSymbolData(const CodeModule *module,
166                                             const SystemInfo *system_info,
167                                             string *symbol_file,
168                                             char **symbol_data,
169                                             size_t *symbol_data_size) {
170  std::string symbol_data_string;
171  SymbolSupplier::SymbolResult result = GetSymbolFile(module,
172                                                      system_info,
173                                                      symbol_file,
174                                                      &symbol_data_string);
175  if (result == FOUND) {
176    *symbol_data_size = symbol_data_string.size() + 1;
177    *symbol_data = new char[*symbol_data_size];
178    if (*symbol_data == NULL) {
179      // Should return INTERRUPT on memory allocation failure.
180      return INTERRUPT;
181    }
182    memcpy(*symbol_data, symbol_data_string.c_str(), symbol_data_string.size());
183    (*symbol_data)[symbol_data_string.size()] = '\0';
184    memory_buffers_.insert(make_pair(module->code_file(), *symbol_data));
185  }
186  return result;
187}
188
189void OnDemandSymbolSupplier::FreeSymbolData(const CodeModule *module) {
190  map<string, char *>::iterator it = memory_buffers_.find(module->code_file());
191  if (it != memory_buffers_.end()) {
192    delete [] it->second;
193    memory_buffers_.erase(it);
194  }
195}
196
197string OnDemandSymbolSupplier::GetLocalModulePath(const CodeModule *module) {
198  NSFileManager *mgr = [NSFileManager defaultManager];
199  const char *moduleStr = module->code_file().c_str();
200  NSString *modulePath =
201    [mgr stringWithFileSystemRepresentation:moduleStr length:strlen(moduleStr)];
202  const char *searchStr = search_dir_.c_str();
203  NSString *searchDir =
204    [mgr stringWithFileSystemRepresentation:searchStr length:strlen(searchStr)];
205
206  if ([mgr fileExistsAtPath:modulePath])
207    return module->code_file();
208
209  // If the module is not found, try to start appending the components to the
210  // search string and stop if a file (not dir) is found or all components
211  // have been appended
212  NSArray *pathComponents = [modulePath componentsSeparatedByString:@"/"];
213  size_t count = [pathComponents count];
214  NSMutableString *path = [NSMutableString string];
215
216  for (size_t i = 0; i < count; ++i) {
217    [path setString:searchDir];
218
219    for (size_t j = 0; j < i + 1; ++j) {
220      size_t idx = count - 1 - i + j;
221      [path appendFormat:@"/%@", [pathComponents objectAtIndex:idx]];
222    }
223
224    BOOL isDir;
225    if ([mgr fileExistsAtPath:path isDirectory:&isDir] && (!isDir)) {
226      return [path fileSystemRepresentation];
227    }
228  }
229
230  return "";
231}
232
233string OnDemandSymbolSupplier::GetModulePath(const CodeModule *module) {
234  return module->code_file();
235}
236
237string OnDemandSymbolSupplier::GetNameForModule(const CodeModule *module) {
238  return PathnameStripper::File(module->code_file());
239}
240
241string OnDemandSymbolSupplier::GetModuleSymbolFile(const CodeModule *module) {
242  string name(GetNameForModule(module));
243  map<string, string>::iterator result = module_file_map_.find(name);
244
245  return (result == module_file_map_.end()) ? "" : (*result).second;
246}
247
248static float GetFileModificationTime(const char *path) {
249  float result = 0;
250  struct stat file_stat;
251  if (stat(path, &file_stat) == 0)
252    result = (float)file_stat.st_mtimespec.tv_sec +
253      (float)file_stat.st_mtimespec.tv_nsec / 1.0e9f;
254
255  return result;
256}
257
258bool OnDemandSymbolSupplier::GenerateSymbolFile(const CodeModule *module,
259                                                const SystemInfo *system_info) {
260  bool result = true;
261  string name = GetNameForModule(module);
262  string module_path = GetLocalModulePath(module);
263  NSString *symbol_path = [NSString stringWithFormat:@"/tmp/%s.%s.sym",
264    name.c_str(), system_info->cpu.c_str()];
265
266  if (module_path.empty())
267    return false;
268
269  // Check if there's already a symbol file cached.  Ensure that the file is
270  // newer than the module.  Otherwise, generate a new one.
271  BOOL generate_file = YES;
272  if ([[NSFileManager defaultManager] fileExistsAtPath:symbol_path]) {
273    // Check if the module file is newer than the saved symbols
274    float cache_time =
275      GetFileModificationTime([symbol_path fileSystemRepresentation]);
276    float module_time =
277      GetFileModificationTime(module_path.c_str());
278
279    if (cache_time > module_time)
280      generate_file = NO;
281  }
282
283  if (generate_file) {
284    DumpSymbols dump(ALL_SYMBOL_DATA, false);
285    if (dump.Read(module_path)) {
286      // What Breakpad calls "x86" should be given to the system as "i386".
287      std::string architecture;
288      if (system_info->cpu.compare("x86") == 0) {
289        architecture = "i386";
290      } else {
291        architecture = system_info->cpu;
292      }
293
294      if (dump.SetArchitecture(architecture)) {
295        std::fstream file([symbol_path fileSystemRepresentation],
296                          std::ios_base::out | std::ios_base::trunc);
297        dump.WriteSymbolFile(file);
298      } else {
299        printf("Architecture %s not available for %s\n",
300               system_info->cpu.c_str(), name.c_str());
301        result = false;
302      }
303    } else {
304      printf("Unable to open %s\n", module_path.c_str());
305      result = false;
306    }
307  }
308
309  // Add the mapping
310  if (result)
311    module_file_map_[name] = [symbol_path fileSystemRepresentation];
312
313  return result;
314}
315