aboutsummaryrefslogblamecommitdiffstats
path: root/erts/emulator/beam/beam_ranges.c
blob: 063b319f7adc243af4d515b626af1141e450bbea (plain) (tree)












































































































































































































                                                                            
/*
 * %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;
	}
    }
}