/* * %CopyrightBegin% * * Copyright Ericsson AB 2012. All Rights Reserved. * * The contents of this file are subject to the Erlang Public License, * Version 1.1, (the "License"); you may not use this file except in * compliance with the License. You should have received a copy of the * Erlang Public License along with this software. If not, it can be * retrieved online at http://www.erlang.org/. * * Software distributed under the License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See * the License for the specific language governing rights and limitations * under the License. * * %CopyrightEnd% */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include "sys.h" #include "erl_vm.h" #include "global.h" #include "beam_load.h" static void lookup_loc(FunctionInfo* fi, BeamInstr* pc, BeamInstr* modp, int idx); /* * The following variables keep a sorted list of address ranges for * each module. It allows us to quickly find a function given an * instruction pointer. */ static Range* modules = NULL; /* Sorted lists of module addresses. */ static int num_loaded_modules; /* Number of loaded modules. */ static int allocated_modules; /* Number of slots allocated. */ static Range* mid_module = NULL; /* Cached search start point */ void erts_init_ranges(void) { allocated_modules = 128; modules = (Range *) erts_alloc(ERTS_ALC_T_MODULE_REFS, allocated_modules*sizeof(Range)); mid_module = modules; num_loaded_modules = 0; } void erts_update_ranges(BeamInstr* code, Uint size) { int i; if (num_loaded_modules == allocated_modules) { allocated_modules *= 2; modules = (Range *) erts_realloc(ERTS_ALC_T_MODULE_REFS, (void *) modules, allocated_modules * sizeof(Range)); } for (i = num_loaded_modules; i > 0; i--) { if (code > modules[i-1].start) { break; } modules[i] = modules[i-1]; } modules[i].start = code; modules[i].end = (BeamInstr *) (((byte *)code) + size); num_loaded_modules++; mid_module = &modules[num_loaded_modules/2]; } void erts_remove_from_ranges(BeamInstr* code) { int i; for (i = 0; i < num_loaded_modules; i++) { if (modules[i].start == code) { num_loaded_modules--; while (i < num_loaded_modules) { modules[i] = modules[i+1]; i++; } mid_module = &modules[num_loaded_modules/2]; return; } } ASSERT(0); /* Not found? */ } Uint erts_ranges_sz(void) { return allocated_modules*sizeof(Range); } /* * Find a function from the given pc and fill information in * the FunctionInfo struct. If the full_info is non-zero, fill * in all available information (including location in the * source code). If no function is found, the 'current' field * will be set to NULL. */ void erts_lookup_function_info(FunctionInfo* fi, BeamInstr* pc, int full_info) { Range* low = modules; Range* high = low + num_loaded_modules; Range* mid = mid_module; fi->current = NULL; fi->needed = 5; fi->loc = LINE_INVALID_LOCATION; while (low < high) { if (pc < mid->start) { high = mid; } else if (pc > mid->end) { low = mid + 1; } else { BeamInstr** low1 = (BeamInstr **) (mid->start + MI_FUNCTIONS); BeamInstr** high1 = low1 + mid->start[MI_NUM_FUNCTIONS]; BeamInstr** mid1; while (low1 < high1) { mid1 = low1 + (high1-low1) / 2; if (pc < mid1[0]) { high1 = mid1; } else if (pc < mid1[1]) { mid_module = mid; fi->current = mid1[0]+2; if (full_info) { BeamInstr** fp = (BeamInstr **) (mid->start + MI_FUNCTIONS); int idx = mid1 - fp; lookup_loc(fi, pc, mid->start, idx); } return; } else { low1 = mid1 + 1; } } return; } mid = low + (high-low) / 2; } } static void lookup_loc(FunctionInfo* fi, BeamInstr* orig_pc, BeamInstr* modp, int idx) { Eterm* line = (Eterm *) modp[MI_LINE_TABLE]; Eterm* low; Eterm* high; Eterm* mid; Eterm pc; if (line == 0) { return; } pc = (Eterm) (BeamInstr) orig_pc; fi->fname_ptr = (Eterm *) (BeamInstr) line[MI_LINE_FNAME_PTR]; low = (Eterm *) (BeamInstr) line[MI_LINE_FUNC_TAB+idx]; high = (Eterm *) (BeamInstr) line[MI_LINE_FUNC_TAB+idx+1]; while (high > low) { mid = low + (high-low) / 2; if (pc < mid[0]) { high = mid; } else if (pc < mid[1]) { int file; int index = mid - (Eterm *) (BeamInstr) line[MI_LINE_FUNC_TAB]; if (line[MI_LINE_LOC_SIZE] == 2) { Uint16* loc_table = (Uint16 *) (BeamInstr) line[MI_LINE_LOC_TAB]; fi->loc = loc_table[index]; } else { Uint32* loc_table = (Uint32 *) (BeamInstr) line[MI_LINE_LOC_TAB]; ASSERT(line[MI_LINE_LOC_SIZE] == 4); fi->loc = loc_table[index]; } if (fi->loc == LINE_INVALID_LOCATION) { return; } fi->needed += 3+2+3+2; file = LOC_FILE(fi->loc); if (file == 0) { /* Special case: Module name with ".erl" appended */ Atom* mod_atom = atom_tab(atom_val(fi->current[0])); fi->needed += 2*(mod_atom->len+4); } else { Atom* ap = atom_tab(atom_val((fi->fname_ptr)[file-1])); fi->needed += 2*ap->len; } return; } else { low = mid + 1; } } }