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#include <sys/stat.h>
31#include <map>
32#include <string>
33#include <iostream>
34#include <fstream>
35#include <utility>
36
37#include "google_breakpad/processor/basic_source_line_resolver.h"
38#include "google_breakpad/processor/minidump.h"
39#include "google_breakpad/processor/system_info.h"
40#include "processor/pathname_stripper.h"
41
42#include "on_demand_symbol_supplier.h"
43#include "common/mac/dump_syms.h"
44
45using std::map;
46using std::string;
47
48using google_breakpad::OnDemandSymbolSupplier;
49using google_breakpad::PathnameStripper;
50using google_breakpad::SymbolSupplier;
51using google_breakpad::SystemInfo;
52
53OnDemandSymbolSupplier::OnDemandSymbolSupplier(const string &search_dir,
54                                               const string &symbol_search_dir)
55  : search_dir_(search_dir) {
56  NSFileManager *mgr = [NSFileManager defaultManager];
57  size_t length = symbol_search_dir.length();
58  if (length) {
59    // Load all sym files in symbol_search_dir into our module_file_map
60    // A symbol file always starts with a line like this:
61    // MODULE mac x86 BBF0A8F9BEADDD2048E6464001CA193F0 GoogleDesktopDaemon
62    // or
63    // MODULE mac ppc BBF0A8F9BEADDD2048E6464001CA193F0 GoogleDesktopDaemon
64    const char *symbolSearchStr = symbol_search_dir.c_str();
65    NSString *symbolSearchPath =
66      [mgr stringWithFileSystemRepresentation:symbolSearchStr
67                                       length:strlen(symbolSearchStr)];
68    NSDirectoryEnumerator *dirEnum = [mgr enumeratorAtPath:symbolSearchPath];
69    NSString *fileName;
70    NSCharacterSet *hexSet =
71      [NSCharacterSet characterSetWithCharactersInString:@"0123456789ABCDEF"];
72    NSCharacterSet *newlineSet =
73      [NSCharacterSet characterSetWithCharactersInString:@"\r\n"];
74    while ((fileName = [dirEnum nextObject])) {
75      // Check to see what type of file we have
76      NSDictionary *attrib = [dirEnum fileAttributes];
77      NSString *fileType = [attrib objectForKey:NSFileType];
78      if ([fileType isEqualToString:NSFileTypeDirectory]) {
79        // Skip subdirectories
80        [dirEnum skipDescendents];
81      } else {
82        NSString *filePath = [symbolSearchPath stringByAppendingPathComponent:fileName];
83        NSString *dataStr = [NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:NULL];
84        if (dataStr) {
85          // Check file to see if it is of appropriate type, and grab module
86          // name.
87          NSScanner *scanner = [NSScanner scannerWithString:dataStr];
88          BOOL goodScan = [scanner scanString:@"MODULE mac " intoString:nil];
89          if (goodScan) {
90            goodScan = ([scanner scanString:@"x86 " intoString:nil] ||
91                        [scanner scanString:@"x86_64 " intoString:nil] ||
92                        [scanner scanString:@"ppc " intoString:nil]);
93            if (goodScan) {
94              NSString *moduleID;
95              goodScan = [scanner scanCharactersFromSet:hexSet
96                                             intoString:&moduleID];
97              if (goodScan) {
98                // Module IDs are always 33 chars long
99                goodScan = [moduleID length] == 33;
100                if (goodScan) {
101                  NSString *moduleName;
102                  goodScan = [scanner scanUpToCharactersFromSet:newlineSet
103                                                     intoString:&moduleName];
104                  if (goodScan) {
105                    goodScan = [moduleName length] > 0;
106                    if (goodScan) {
107                      const char *moduleNameStr = [moduleName UTF8String];
108                      const char *filePathStr = [filePath fileSystemRepresentation];
109                      // Map our file
110                      module_file_map_[moduleNameStr] = filePathStr;
111                    }
112                  }
113                }
114              }
115            }
116          }
117        }
118      }
119    }
120  }
121}
122
123SymbolSupplier::SymbolResult
124OnDemandSymbolSupplier::GetSymbolFile(const CodeModule *module,
125                                      const SystemInfo *system_info,
126                                      string *symbol_file) {
127  string path(GetModuleSymbolFile(module));
128
129  if (path.empty()) {
130    if (!GenerateSymbolFile(module, system_info))
131      return NOT_FOUND;
132
133    path = GetModuleSymbolFile(module);
134  }
135
136  if (path.empty())
137    return NOT_FOUND;
138
139  *symbol_file = path;
140  return FOUND;
141}
142
143SymbolSupplier::SymbolResult
144OnDemandSymbolSupplier::GetSymbolFile(const CodeModule *module,
145                                      const SystemInfo *system_info,
146                                      string *symbol_file,
147                                      string *symbol_data) {
148  SymbolSupplier::SymbolResult s = GetSymbolFile(module,
149                                                 system_info,
150                                                 symbol_file);
151
152
153  if (s == FOUND) {
154    std::ifstream in(symbol_file->c_str());
155    getline(in, *symbol_data, std::string::traits_type::to_char_type(
156                std::string::traits_type::eof()));
157    in.close();
158  }
159
160  return s;
161}
162
163SymbolSupplier::SymbolResult
164OnDemandSymbolSupplier::GetCStringSymbolData(const CodeModule *module,
165                                             const SystemInfo *system_info,
166                                             string *symbol_file,
167                                             char **symbol_data,
168                                             size_t *symbol_data_size) {
169  std::string symbol_data_string;
170  SymbolSupplier::SymbolResult result = GetSymbolFile(module,
171                                                      system_info,
172                                                      symbol_file,
173                                                      &symbol_data_string);
174  if (result == FOUND) {
175    *symbol_data_size = symbol_data_string.size() + 1;
176    *symbol_data = new char[*symbol_data_size];
177    if (*symbol_data == NULL) {
178      // Should return INTERRUPT on memory allocation failure.
179      return INTERRUPT;
180    }
181    memcpy(*symbol_data, symbol_data_string.c_str(), symbol_data_string.size());
182    (*symbol_data)[symbol_data_string.size()] = '\0';
183    memory_buffers_.insert(make_pair(module->code_file(), *symbol_data));
184  }
185  return result;
186}
187
188void OnDemandSymbolSupplier::FreeSymbolData(const CodeModule *module) {
189  map<string, char *>::iterator it = memory_buffers_.find(module->code_file());
190  if (it != memory_buffers_.end()) {
191    delete [] it->second;
192    memory_buffers_.erase(it);
193  }
194}
195
196string OnDemandSymbolSupplier::GetLocalModulePath(const CodeModule *module) {
197  NSFileManager *mgr = [NSFileManager defaultManager];
198  const char *moduleStr = module->code_file().c_str();
199  NSString *modulePath =
200    [mgr stringWithFileSystemRepresentation:moduleStr length:strlen(moduleStr)];
201  const char *searchStr = search_dir_.c_str();
202  NSString *searchDir =
203    [mgr stringWithFileSystemRepresentation:searchStr length:strlen(searchStr)];
204
205  if ([mgr fileExistsAtPath:modulePath])
206    return module->code_file();
207
208  // If the module is not found, try to start appending the components to the
209  // search string and stop if a file (not dir) is found or all components
210  // have been appended
211  NSArray *pathComponents = [modulePath componentsSeparatedByString:@"/"];
212  size_t count = [pathComponents count];
213  NSMutableString *path = [NSMutableString string];
214
215  for (size_t i = 0; i < count; ++i) {
216    [path setString:searchDir];
217
218    for (size_t j = 0; j < i + 1; ++j) {
219      size_t idx = count - 1 - i + j;
220      [path appendFormat:@"/%@", [pathComponents objectAtIndex:idx]];
221    }
222
223    BOOL isDir;
224    if ([mgr fileExistsAtPath:path isDirectory:&isDir] && (!isDir)) {
225      return [path fileSystemRepresentation];
226    }
227  }
228
229  return "";
230}
231
232string OnDemandSymbolSupplier::GetModulePath(const CodeModule *module) {
233  return module->code_file();
234}
235
236string OnDemandSymbolSupplier::GetNameForModule(const CodeModule *module) {
237  return PathnameStripper::File(module->code_file());
238}
239
240string OnDemandSymbolSupplier::GetModuleSymbolFile(const CodeModule *module) {
241  string name(GetNameForModule(module));
242  map<string, string>::iterator result = module_file_map_.find(name);
243
244  return (result == module_file_map_.end()) ? "" : (*result).second;
245}
246
247static float GetFileModificationTime(const char *path) {
248  float result = 0;
249  struct stat file_stat;
250  if (stat(path, &file_stat) == 0)
251    result = (float)file_stat.st_mtimespec.tv_sec +
252      (float)file_stat.st_mtimespec.tv_nsec / 1.0e9f;
253
254  return result;
255}
256
257bool OnDemandSymbolSupplier::GenerateSymbolFile(const CodeModule *module,
258                                                const SystemInfo *system_info) {
259  bool result = true;
260  string name = GetNameForModule(module);
261  string module_path = GetLocalModulePath(module);
262  NSString *symbol_path = [NSString stringWithFormat:@"/tmp/%s.%s.sym",
263    name.c_str(), system_info->cpu.c_str()];
264
265  if (module_path.empty())
266    return false;
267
268  // Check if there's already a symbol file cached.  Ensure that the file is
269  // newer than the module.  Otherwise, generate a new one.
270  BOOL generate_file = YES;
271  if ([[NSFileManager defaultManager] fileExistsAtPath:symbol_path]) {
272    // Check if the module file is newer than the saved symbols
273    float cache_time =
274      GetFileModificationTime([symbol_path fileSystemRepresentation]);
275    float module_time =
276      GetFileModificationTime(module_path.c_str());
277
278    if (cache_time > module_time)
279      generate_file = NO;
280  }
281
282  if (generate_file) {
283    NSString *module_str = [[NSFileManager defaultManager]
284      stringWithFileSystemRepresentation:module_path.c_str()
285                                  length:module_path.length()];
286    DumpSymbols dump(ALL_SYMBOL_DATA, false);
287    if (dump.Read(module_str)) {
288      // What Breakpad calls "x86" should be given to the system as "i386".
289      std::string architecture;
290      if (system_info->cpu.compare("x86") == 0) {
291        architecture = "i386";
292      } else {
293        architecture = system_info->cpu;
294      }
295
296      if (dump.SetArchitecture(architecture)) {
297        std::fstream file([symbol_path fileSystemRepresentation],
298                          std::ios_base::out | std::ios_base::trunc);
299        dump.WriteSymbolFile(file);
300      } else {
301        printf("Architecture %s not available for %s\n",
302               system_info->cpu.c_str(), name.c_str());
303        result = false;
304      }
305    } else {
306      printf("Unable to open %s\n", [module_str UTF8String]);
307      result = false;
308    }
309  }
310
311  // Add the mapping
312  if (result)
313    module_file_map_[name] = [symbol_path fileSystemRepresentation];
314
315  return result;
316}
317