Skip to main content
summaryrefslogblamecommitdiffstats
blob: 3f920727354a2a7d85cef40301b08770412869ca (plain) (tree)
1
2
3
4
5
6
7
8
9








                                                                                
                                                                          











                                                                                 
                   




                           






                                 
 
                                             













                                              
                  





























                                                                                                  
                                   


















































                                                                             
                                                                                   


                                           
                                                                                       





                                                                                     
                                                                                                     

                                                                               
                                                                                                   















































                                                                                                 
                                          

 

                                                                     







                                    
                                                        
                                           



                                                                                
                                                                                                    











                                                                         

                                   

                           
                                                                                                       
                                            







                                          






















                                                                                  

          






                                                     
                                                              





                                                            
                                                                               


                                                           
                                                                               


                                                            
                                                               
























                                                                
/*******************************************************************************
 * Copyright (c) 2010 Wind River Systems, Inc. and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * and Eclipse Distribution License v1.0 which accompany this distribution.
 * The Eclipse Public License is available at
 * http://www.eclipse.org/legal/epl-v10.html
 * and the Eclipse Distribution License is available at
 * http://www.eclipse.org/org/documents/edl-v10.php.
 * You may elect to redistribute this code under either of these licenses.
 *
 * Contributors:
 *     Wind River Systems - initial API and implementation
 *******************************************************************************/

/*
 * TCF service line Numbers - proxy version.
 *
 * The service associates locations in the source files with the corresponding
 * machine instruction addresses in the executable object.
 */

#include <config.h>

#if ENABLE_LineNumbersProxy

#include <assert.h>
#include <stdio.h>
#include <framework/context.h>
#include <framework/cache.h>
#include <framework/json.h>
#include <framework/events.h>
#include <framework/myalloc.h>
#include <framework/exceptions.h>
#include <services/linenumbers.h>

#define HASH_SIZE (16 * MEM_USAGE_FACTOR - 1)

/* Line numbers cahce, one per channel */
typedef struct LineNumbersCache {
    unsigned magic;
    Channel * channel;
    LINK link_root;
    LINK link_addr[HASH_SIZE];
} LineNumbersCache;

/* Line number to address translation cache */
typedef struct LineAddressCache {
    unsigned magic;
    LINK link_cache;
    AbstractCache cache;
    Context * ctx;
    char * file;
    int line;
    int column;
    ReplyHandlerInfo * pending;
    ErrorReport * error;
    int areas_cnt;
    CodeArea * areas;
    int disposed;
} LineAddressCache;

#define LINE_NUMBERS_CACHE_MAGIC 0x19873654

#define root2cache(A) ((LineNumbersCache *)((char *)(A) - offsetof(LineNumbersCache, link_root)))
#define cache2addr(A) ((LineAddressCache *)((char *)(A) - offsetof(LineAddressCache, link_cache)))

static LINK root;

static int code_area_cnt = 0;
static int code_area_max = 0;
static CodeArea * code_area_buf = NULL;

static void free_line_address_cache(LineAddressCache * cache) {
    assert(cache->magic == LINE_NUMBERS_CACHE_MAGIC);
    list_remove(&cache->link_cache);
    cache->disposed = 1;
    if (cache->pending == NULL) {
        int i;
        cache->magic = 0;
        cache_dispose(&cache->cache);
        release_error_report(cache->error);
        context_unlock(cache->ctx);
        for (i = 0; i < cache->areas_cnt; i++) {
            CodeArea * area = cache->areas + i;
            loc_free(area->file);
            loc_free(area->directory);
        }
        loc_free(cache->areas);
        loc_free(cache->file);
        loc_free(cache);
    }
}

static void free_line_numbers_cache(LineNumbersCache * cache) {
    int i;
    assert(cache->magic == LINE_NUMBERS_CACHE_MAGIC);
    cache->magic = 0;
    for (i = 0; i < HASH_SIZE; i++) {
        while (!list_is_empty(cache->link_addr + i)) {
            free_line_address_cache(cache2addr(cache->link_addr[i].next));
        }
    }
    channel_unlock(cache->channel);
    list_remove(&cache->link_root);
    loc_free(cache);
}

static LineNumbersCache * get_line_numbers_cache(void) {
    LINK * l = NULL;
    LineNumbersCache * cache = NULL;
    Channel * c = cache_channel();
    if (c == NULL) exception(ERR_SYM_NOT_FOUND);
    for (l = root.next; l != &root; l = l->next) {
        LineNumbersCache * x = root2cache(l);
        if (x->channel == c) {
            cache = x;
            break;
        }
    }
    if (cache == NULL) {
        int i = 0;
        cache = (LineNumbersCache *)loc_alloc_zero(sizeof(LineNumbersCache));
        cache->magic = LINE_NUMBERS_CACHE_MAGIC;
        cache->channel = c;
        list_add_first(&cache->link_root, &root);
        for (i = 0; i < HASH_SIZE; i++) {
            list_init(cache->link_addr + i);
        }
        channel_lock(c);
    }
    return cache;
}

static unsigned hash_addr(Context * ctx, const char * file, int line, int column) {
    int i;
    unsigned h = 0;
    for (i = 0; file[i]; i++) h += file[i];
    return (h + ((uintptr_t)ctx >> 4) + (unsigned)line + (unsigned)column) % HASH_SIZE;
}

static void read_code_area_props(InputStream * inp, const char * name, void * args) {
    CodeArea * area = (CodeArea *)args;
    if (strcmp(name, "SLine") == 0) area->start_line = json_read_long(inp);
    else if (strcmp(name, "SCol") == 0) area->start_column = json_read_long(inp);
    else if (strcmp(name, "SAddr") == 0) area->start_address = (ContextAddress)json_read_uint64(inp);
    else if (strcmp(name, "ELine") == 0) area->end_line = json_read_long(inp);
    else if (strcmp(name, "ECol") == 0) area->end_column = json_read_long(inp);
    else if (strcmp(name, "EAddr") == 0) area->end_address = (ContextAddress)json_read_uint64(inp);
    else if (strcmp(name, "File") == 0) area->file = json_read_alloc_string(inp);
    else if (strcmp(name, "Dir") == 0) area->directory = json_read_alloc_string(inp);
    else if (strcmp(name, "ISA") == 0) area->isa = json_read_long(inp);
    else if (strcmp(name, "IsStmt") == 0) area->is_statement = json_read_boolean(inp);
    else if (strcmp(name, "BasicBlock") == 0) area->basic_block = json_read_boolean(inp);
    else if (strcmp(name, "PrologueEnd") == 0) area->prologue_end = json_read_boolean(inp);
    else if (strcmp(name, "EpilogueBegin") == 0) area->epilogue_begin = json_read_boolean(inp);
}

static void read_code_area_array(InputStream * inp, void * args) {
    CodeArea * area = NULL;
    if (code_area_cnt >= code_area_max) {
        code_area_max += 8;
        code_area_buf = (CodeArea *)loc_realloc(code_area_buf, sizeof(CodeArea) * code_area_max);
    }
    area = code_area_buf + code_area_cnt++;
    memset(area, 0, sizeof(CodeArea));
    json_read_struct(inp, read_code_area_props, area);
}

static void validate_map_to_memory(Channel * c, void * args, int error) {
    Trap trap;
    LineAddressCache * f = (LineAddressCache *)args;
    assert(f->magic == LINE_NUMBERS_CACHE_MAGIC);
    assert(f->pending != NULL);
    assert(f->error == NULL);
    if (set_trap(&trap)) {
        f->pending = NULL;
        if (!error) {
            error = read_errno(&c->inp);
            code_area_cnt = 0;
            json_read_array(&c->inp, read_code_area_array, NULL);
            if (read_stream(&c->inp) != 0) exception(ERR_JSON_SYNTAX);
            if (read_stream(&c->inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
            if (code_area_cnt > 0) {
                f->areas_cnt = code_area_cnt;
                f->areas = (CodeArea *)loc_alloc(sizeof(CodeArea) * code_area_cnt);
                memcpy(f->areas, code_area_buf, sizeof(CodeArea) * code_area_cnt);
            }
        }
        clear_trap(&trap);
    }
    else {
        error = trap.error;
    }
    f->error = get_error_report(error);
    cache_notify(&f->cache);
    if (f->disposed) free_line_address_cache(f);
    if (trap.error) exception(trap.error);
}

int line_to_address(Context * ctx, char * file, int line, int column,
                    LineNumbersCallBack * client, void * args) {
    LINK * l = NULL;
    LineNumbersCache * cache = NULL;
    LineAddressCache * f = NULL;
    unsigned h;
    Trap trap;

    if (!set_trap(&trap)) return -1;

    ctx = context_get_group(ctx, CONTEXT_GROUP_PROCESS);
    h = hash_addr(ctx, file, line, column);
    cache = get_line_numbers_cache();
    assert(cache->magic == LINE_NUMBERS_CACHE_MAGIC);
    for (l = cache->link_addr[h].next; l != cache->link_addr + h; l = l->next) {
        LineAddressCache * c = cache2addr(l);
        if (c->ctx == ctx && c->line == line && c->column == column && strcmp(c->file, file) == 0) {
            assert(c->magic == LINE_NUMBERS_CACHE_MAGIC);
            f = c;
            break;
        }
    }

    if (f == NULL) {
        Channel * c = cache_channel();
        if (c == NULL) exception(ERR_UNSUPPORTED);
        f = (LineAddressCache *)loc_alloc_zero(sizeof(LineAddressCache));
        list_add_first(&f->link_cache, cache->link_addr + h);
        f->magic = LINE_NUMBERS_CACHE_MAGIC;
        context_lock(f->ctx = ctx);
        f->file = loc_strdup(file);
        f->line = line;
        f->column = column;
        f->pending = protocol_send_command(c, "LineNumbers", "mapToMemory", validate_map_to_memory, f);
        json_write_string(&c->out, ctx->id);
        write_stream(&c->out, 0);
        json_write_string(&c->out, file);
        write_stream(&c->out, 0);
        json_write_long(&c->out, line);
        write_stream(&c->out, 0);
        json_write_long(&c->out, column);
        write_stream(&c->out, 0);
        write_stream(&c->out, MARKER_EOM);
        cache_wait(&f->cache);
    }
    else if (f->pending != NULL) {
        cache_wait(&f->cache);
    }
    else if (f->error != NULL) {
        char msg[FILE_PATH_SIZE + 64];
        snprintf(msg, sizeof(msg), "Text position '%s:%d' not found", file, line);
        exception(set_errno(set_error_report_errno(f->error), msg));
    }
    else {
        int i;
        for (i = 0; i < f->areas_cnt; i++) {
            client(f->areas + i, args);
        }
    }
    clear_trap(&trap);
    return 0;
}

static void flush_cache(Context * ctx) {
    LINK * l;
    LINK * m;
    int i;

    for (m = root.next; m != &root; m = m->next) {
        LineNumbersCache * cache = root2cache(m);
        for (i = 0; i < HASH_SIZE; i++) {
            l = cache->link_addr[i].next;
            while (l != cache->link_addr + i) {
                LineAddressCache * c = cache2addr(l);
                l = l->next;
                if (c->ctx == ctx) free_line_address_cache(c);
            }
        }
    }
}

static void event_context_created(Context * ctx, void * x) {
    if (ctx == context_get_group(ctx, CONTEXT_GROUP_PROCESS)) flush_cache(ctx);
}

static void event_context_exited(Context * ctx, void * x) {
    if (ctx == context_get_group(ctx, CONTEXT_GROUP_PROCESS)) flush_cache(ctx);
}

static void event_context_changed(Context * ctx, void * x) {
    flush_cache(context_get_group(ctx, CONTEXT_GROUP_PROCESS));
}

static void channel_close_listener(Channel * c) {
    LINK * l = root.next;
    while (l != &root) {
        LineNumbersCache * cache = root2cache(l);
        l = l->next;
        if (cache->channel == c) free_line_numbers_cache(cache);
    }
}

void ini_line_numbers_lib(void) {
    static ContextEventListener listener = {
        event_context_created,
        event_context_exited,
        NULL,
        NULL,
        event_context_changed
    };
    list_init(&root);
    add_context_event_listener(&listener, NULL);
    add_channel_close_listener(channel_close_listener);
}

#endif /* ENABLE_LineNumbersProxy */

Back to the top