po: Run make update-po to update the .po files
[memprof.git] / src / binfile.c
blobf3286181e233eed17a7e1158b0dab61b22bc14fe
1 /* MemProf -- memory profiler and leak detector
2 * Copyright 1999, 2000, 2001, Red Hat, Inc.
3 * Copyright 2002, Kristian Rietveld
5 * Sysprof -- Sampling, systemwide CPU profiler
6 * Copyright 2004, 2005, 2006, 2007, Soeren Sandmann
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
23 /* Most interesting code in this file is lifted from bfdutils.c
24 * and process.c from Memprof,
26 #include "config.h"
28 #include <glib.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <unistd.h>
32 #include <fcntl.h>
33 #include <stdio.h>
34 #include <sys/types.h>
35 #include <sys/stat.h>
37 #include "binfile.h"
38 #include "elfparser.h"
39 #include "process.h"
41 struct BinFile
43 int ref_count;
45 ElfParser * elf;
47 char * filename;
49 char * undefined_name;
51 gulong text_offset;
53 gboolean inode_check;
54 ino_t inode;
57 static ino_t
58 get_inode (const char *filename)
60 struct stat statbuf;
62 if (strcmp (filename, "[vdso]") == 0)
63 return (ino_t)0;
65 if (stat (filename, &statbuf) < 0)
66 return (ino_t)-1;
68 return statbuf.st_ino;
71 static gboolean
72 already_warned (const char *name)
74 static GPtrArray *warnings;
75 int i;
77 if (!warnings)
78 warnings = g_ptr_array_new ();
80 for (i = 0; i < warnings->len; ++i)
82 if (strcmp (warnings->pdata[i], name) == 0)
83 return TRUE;
86 g_ptr_array_add (warnings, g_strdup (name));
88 return FALSE;
91 static ElfParser *
92 separate_debug_file_exists (const char *name, guint32 crc)
94 guint32 file_crc;
95 ElfParser *parser = elf_parser_new (name, NULL);
97 #if 0
98 g_print (" trying %s: ", name);
99 #endif
100 if (!parser)
102 #if 0
103 g_print ("no.\n");
104 #endif
105 return NULL;
108 file_crc = elf_parser_get_crc32 (parser);
110 if (file_crc != crc)
112 if (!already_warned (name))
113 g_print ("warning: %s has wrong crc \n", name);
115 elf_parser_free (parser);
117 return NULL;
120 #if 0
121 g_print ("found\n");
122 #endif
124 return parser;
127 static const char *const debug_file_directory = DEBUGDIR;
129 static ElfParser *
130 get_debug_file (ElfParser *elf,
131 const char *filename,
132 char **new_name)
134 #define N_TRIES 4
135 const char *basename;
136 char *dir;
137 guint32 crc32;
138 char *tries[N_TRIES];
139 int i;
140 ElfParser *result;
142 if (!elf)
143 return NULL;
145 basename = elf_parser_get_debug_link (elf, &crc32);
147 #if 0
148 g_print (" debug link for %s is %s\n", filename, basename);
149 #endif
151 if (!basename)
152 return NULL;
154 dir = g_path_get_dirname (filename);
156 tries[0] = g_build_filename (dir, basename, NULL);
157 tries[1] = g_build_filename (dir, ".debug", basename, NULL);
158 tries[2] = g_build_filename ("/usr", "lib", "debug", dir, basename, NULL);
159 tries[3] = g_build_filename (debug_file_directory, dir, basename, NULL);
161 for (i = 0; i < N_TRIES; ++i)
163 #if 0
164 g_print ("trying: %s\n", tries[i]);
165 #endif
166 result = separate_debug_file_exists (tries[i], crc32);
167 if (result)
169 #if 0
170 g_print (" found debug binary for %s: %s\n", filename, tries[i]);
171 #endif
172 if (new_name)
173 *new_name = g_strdup (tries[i]);
174 break;
178 g_free (dir);
180 for (i = 0; i < N_TRIES; ++i)
181 g_free (tries[i]);
183 return result;
186 static gboolean
187 list_contains_name (GList *names, const char *name)
189 GList *list;
190 for (list = names; list != NULL; list = list->next)
192 const char *n = list->data;
194 if (strcmp (n, name) == 0)
195 return TRUE;
198 return FALSE;
201 static ElfParser *
202 find_separate_debug_file (ElfParser *elf,
203 const char *filename)
205 ElfParser *debug;
206 char *debug_name = NULL;
207 char *fname;
208 GList *seen_names = NULL;
210 fname = g_strdup (filename);
214 if (list_contains_name (seen_names, fname))
216 #if 0
217 g_print (" cycle detected\n");
218 #endif
219 /* cycle detected, just return the original elf file itself */
220 break;
223 debug = get_debug_file (elf, fname, &debug_name);
225 if (debug)
227 elf_parser_free (elf);
228 elf = debug;
230 seen_names = g_list_prepend (seen_names, fname);
231 fname = debug_name;
233 #if 0
234 else
236 g_print (" no debug info file for %s\n", fname);
238 #endif
240 while (debug);
242 g_free (fname);
243 g_list_foreach (seen_names, (GFunc)g_free, NULL);
244 g_list_free (seen_names);
246 return elf;
249 static GHashTable *bin_files;
251 BinFile *
252 bin_file_new (const char *filename)
254 /* FIXME: should be able to return an error */
255 BinFile *bf;
257 if (!bin_files)
258 bin_files = g_hash_table_new (g_str_hash, g_str_equal);
260 bf = g_hash_table_lookup (bin_files, filename);
262 if (bf)
264 bf->ref_count++;
266 else
268 bf = g_new0 (BinFile, 1);
270 bf->inode_check = FALSE;
271 bf->filename = g_strdup (filename);
272 bf->undefined_name = g_strdup_printf ("In file %s", filename);
273 bf->ref_count = 1;
275 g_hash_table_insert (bin_files, bf->filename, bf);
277 if (strcmp (filename, "[vdso]") == 0)
279 #if 0
280 gsize length;
281 const guint8 *vdso_bytes;
283 vdso_bytes = process_get_vdso_bytes (&length);
284 if (vdso_bytes)
286 bf->elf = elf_parser_new_from_data (vdso_bytes, length);
287 #if 0
288 g_print ("got vdso elf: %p (%d)\n", bf->elf, length);
289 #endif
291 else
292 #endif
293 bf->elf = NULL;
295 else
297 bf->elf = elf_parser_new (filename, NULL);
298 #if 0
299 if (!bf->elf)
300 g_print ("Could not parse file %s\n", filename);
301 #endif
304 /* We need the text offset of the actual binary, not the
305 * (potential) debug binary
307 if (bf->elf)
309 ElfParser *oldelf;
311 bf->text_offset = elf_parser_get_text_offset (bf->elf);
312 #if 0
313 g_print ("text offset: %d\n", bf->text_offset);
314 #endif
316 oldelf = bf->elf;
317 #if 0
318 if (bf->elf)
319 g_print ("trying to find separate debug file for %s\n", filename);
320 #endif
321 bf->elf = find_separate_debug_file (bf->elf, filename);
323 #if 0
324 if (!bf->elf)
325 g_print (" returned NULL\n");
326 else if (bf->elf != oldelf)
327 g_print (" successfully opened a different elf file than the original\n");
328 else
329 g_print (" opened the original elf file\n");
330 #endif
331 bf->inode = get_inode (filename);
335 return bf;
338 void
339 bin_file_free (BinFile *bin_file)
341 if (--bin_file->ref_count == 0)
343 g_hash_table_remove (bin_files, bin_file->filename);
345 if (bin_file->elf)
346 elf_parser_free (bin_file->elf);
348 g_free (bin_file->filename);
349 g_free (bin_file->undefined_name);
350 g_free (bin_file);
354 const BinSymbol *
355 bin_file_lookup_symbol (BinFile *bin_file,
356 gsize address)
358 if (bin_file->elf)
360 #if 0
361 g_print ("bin file lookup lookup %d\n", address);
362 #endif
363 address -= bin_file->text_offset;
365 const ElfSym *sym = elf_parser_lookup_symbol (bin_file->elf, address);
367 #if 0
368 g_print ("lookup %d in %s\n", address, bin_file->filename);
369 #endif
371 if (sym)
373 #if 0
374 g_print ("found %lx => %s\n", address, bin_symbol_get_name (bin_file, sym));
375 #endif
376 return (const BinSymbol *)sym;
379 #if 0
380 else
381 g_print ("no elf file for %s\n", bin_file->filename);
382 #endif
384 #if 0
385 g_print ("%lx undefined in %s (textoffset %d)\n", address + bin_file->text_offset, bin_file->filename, bin_file->text_offset);
386 #endif
388 return (const BinSymbol *)bin_file->undefined_name;
391 gboolean
392 bin_file_check_inode (BinFile *bin_file,
393 ino_t inode)
395 if (bin_file->inode == inode)
396 return TRUE;
398 if (!bin_file->elf)
399 return FALSE;
401 if (!bin_file->inode_check)
403 g_print ("warning: Inode mismatch for %s (disk: %lld, memory: %lld)\n",
404 bin_file->filename,
405 (guint64)bin_file->inode, (guint64)inode);
407 bin_file->inode_check = TRUE;
410 return FALSE;
413 const char *
414 bin_symbol_get_name (BinFile *file,
415 const BinSymbol *symbol)
417 if (file->undefined_name == (char *)symbol)
418 return file->undefined_name;
419 else
420 return elf_parser_get_sym_name (file->elf, (const ElfSym *)symbol);