blob: 348c53d2a7546feec3c0188d0beaacc0ee58f035 [file]
/*******************************************************************************
* Copyright (c) 2009, 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.
*
* Contributors:
* Wind River Systems - initial API and implementation
*******************************************************************************/
/*
* This module holds execution context memory maps.
*/
#include <config.h>
#if SERVICE_MemoryMap
#include <assert.h>
#include <errno.h>
#include <stdio.h>
#include <framework/myalloc.h>
#include <framework/trace.h>
#include <framework/json.h>
#include <framework/events.h>
#include <framework/exceptions.h>
#include <services/memorymap.h>
typedef struct Listener {
MemoryMapEventListener * listener;
void * args;
} Listener;
typedef struct ContextExtensionMM {
int valid;
ErrorReport * error;
MemoryMap target_map;
MemoryMap client_map;
} ContextExtensionMM;
typedef struct ClientMap {
LINK link_list;
LINK link_hash;
LINK link_ctx;
char * id;
MemoryMap map;
Channel * channel;
} ClientMap;
static size_t context_extension_offset = 0;
#define EXT(ctx) ((ContextExtensionMM *)((char *)(ctx) + context_extension_offset))
static const char MEMORY_MAP[] = "MemoryMap";
static Listener * listeners = NULL;
static unsigned listener_cnt = 0;
static unsigned listener_max = 0;
#define list2map(A) ((ClientMap *)((char *)(A) - offsetof(ClientMap, link_list)))
#define hash2map(A) ((ClientMap *)((char *)(A) - offsetof(ClientMap, link_hash)))
#define ctx2map(A) ((ClientMap *)((char *)(A) - offsetof(ClientMap, link_ctx)))
#define MAP_HASH_SIZE (4 * MEM_USAGE_FACTOR - 1)
static LINK client_map_list;
static LINK client_map_hash[MAP_HASH_SIZE];
static TCFBroadcastGroup * broadcast_group = NULL;
static unsigned map_id_hash(const char * id) {
int i;
unsigned h = 0;
for (i = 0; id[i]; i++) h += id[i];
return h % MAP_HASH_SIZE;
}
static MemoryRegion * add_region(MemoryMap * map) {
MemoryRegion * r = NULL;
if (map->region_cnt >= map->region_max) {
map->region_max += 8;
map->regions = (MemoryRegion *)loc_realloc(map->regions, sizeof(MemoryRegion) * map->region_max);
}
r = map->regions + map->region_cnt++;
memset(r, 0, sizeof(MemoryRegion));
return r;
}
static int str_equ(char * x, char * y) {
if (x == y) return 1;
if (x == NULL) return 0;
if (y == NULL) return 0;
return strcmp(x, y) == 0;
}
static int update_context_client_map(Context * ctx) {
ContextExtensionMM * ext = EXT(ctx);
unsigned r_cnt = 0;
int equ = 0;
unsigned h, i;
LINK * l;
LINK maps;
assert(ctx == context_get_group(ctx, CONTEXT_GROUP_PROCESS));
list_init(&maps);
h = map_id_hash(ctx->id);
for (l = client_map_hash[h].next; l != &client_map_hash[h]; l = l->next) {
ClientMap * m = hash2map(l);
if (strcmp(m->id, ctx->id) == 0) {
list_add_last(&m->link_ctx, &maps);
r_cnt += m->map.region_cnt;
}
}
if (ctx->name != NULL) {
h = map_id_hash(ctx->name);
for (l = client_map_hash[h].next; l != &client_map_hash[h]; l = l->next) {
ClientMap * m = hash2map(l);
if (strcmp(m->id, ctx->name) == 0) {
list_add_last(&m->link_ctx, &maps);
r_cnt += m->map.region_cnt;
}
}
}
equ = ext->client_map.region_cnt == r_cnt;
if (equ) {
unsigned k = 0;
for (l = maps.next; equ && l != &maps; l = l->next) {
ClientMap * m = ctx2map(l);
for (i = 0; equ && i < m->map.region_cnt; i++) {
MemoryRegion * x = m->map.regions + i;
MemoryRegion * y = ext->client_map.regions + k++;
equ =
y->addr == x->addr &&
y->size == x->size &&
y->file_offs == x->file_offs &&
y->bss == x->bss &&
y->flags == x->flags &&
str_equ(y->file_name, x->file_name) &&
str_equ(y->sect_name, x->sect_name);
}
}
}
if (equ) return 0;
context_clear_memory_map(&ext->client_map);
for (l = maps.next; l != &maps; l = l->next) {
ClientMap * m = ctx2map(l);
for (i = 0; i < m->map.region_cnt; i++) {
MemoryRegion * x = m->map.regions + i;
MemoryRegion * y = add_region(&ext->client_map);
y->addr = x->addr;
y->size = x->size;
y->file_offs = x->file_offs;
y->bss = x->bss;
y->flags = x->flags;
if (x->file_name) y->file_name = loc_strdup(x->file_name);
if (x->sect_name) y->sect_name = loc_strdup(x->sect_name);
if (x->id) y->id = loc_strdup(x->id);
}
}
return 1;
}
static void update_all_context_client_maps(void) {
LINK * l;
for (l = context_root.next; l != &context_root; l = l->next) {
Context * ctx = ctxl2ctxp(l);
if (ctx->exited) continue;
if (ctx != context_get_group(ctx, CONTEXT_GROUP_PROCESS)) continue;
if (update_context_client_map(ctx)) send_context_changed_event(ctx);
}
}
static void event_memory_map_changed(Context * ctx, void * args) {
OutputStream * out;
ContextExtensionMM * ext = EXT(ctx);
if (ctx != context_get_group(ctx, CONTEXT_GROUP_PROCESS)) return;
update_context_client_map(ctx);
context_clear_memory_map(&ext->target_map);
if (!ext->valid) return;
ext->valid = 0;
if (ctx->exited) return;
out = &broadcast_group->out;
write_stringz(out, "E");
write_stringz(out, MEMORY_MAP);
write_stringz(out, "changed");
json_write_string(out, ctx->id);
write_stream(out, 0);
write_stream(out, MARKER_EOM);
}
static void event_context_disposed(Context * ctx, void * args) {
MemoryMap * map;
ContextExtensionMM * ext = EXT(ctx);
map = &ext->target_map;
context_clear_memory_map(map);
loc_free(map->regions);
memset(map, 0, sizeof(MemoryMap));
map = &ext->client_map;
context_clear_memory_map(map);
loc_free(map->regions);
memset(map, 0, sizeof(MemoryMap));
release_error_report(ext->error);
}
int memory_map_get(Context * ctx, MemoryMap ** client_map, MemoryMap ** target_map) {
ContextExtensionMM * ext = EXT(ctx);
assert(ctx == context_get_group(ctx, CONTEXT_GROUP_PROCESS));
if (!ext->valid) {
context_clear_memory_map(&ext->target_map);
release_error_report(ext->error);
if (context_get_memory_map(ctx, &ext->target_map) < 0) {
ext->error = get_error_report(errno);
ext->valid = get_error_code(errno) != ERR_CACHE_MISS;
}
else {
ext->error = NULL;
ext->valid = 1;
}
}
if (ext->error != NULL) {
set_error_report_errno(ext->error);
return -1;
}
*client_map = &ext->client_map;
*target_map = &ext->target_map;
return 0;
}
void memory_map_event_module_loaded(Context * ctx) {
unsigned i;
assert(ctx->ref_count > 0);
assert(ctx == context_get_group(ctx, CONTEXT_GROUP_PROCESS));
event_memory_map_changed(ctx, NULL);
for (i = 0; i < listener_cnt; i++) {
Listener * l = listeners + i;
if (l->listener->module_loaded == NULL) continue;
l->listener->module_loaded(ctx, l->args);
}
}
void memory_map_event_code_section_ummapped(Context * ctx, ContextAddress addr, ContextAddress size) {
unsigned i;
assert(ctx->ref_count > 0);
assert(ctx == context_get_group(ctx, CONTEXT_GROUP_PROCESS));
event_memory_map_changed(ctx, NULL);
for (i = 0; i < listener_cnt; i++) {
Listener * l = listeners + i;
if (l->listener->code_section_ummapped == NULL) continue;
l->listener->code_section_ummapped(ctx, addr, size, l->args);
}
}
void memory_map_event_module_unloaded(Context * ctx) {
unsigned i;
assert(ctx->ref_count > 0);
assert(ctx == context_get_group(ctx, CONTEXT_GROUP_PROCESS));
event_memory_map_changed(ctx, NULL);
for (i = 0; i < listener_cnt; i++) {
Listener * l = listeners + i;
if (l->listener->module_unloaded == NULL) continue;
l->listener->module_unloaded(ctx, l->args);
}
}
void add_memory_map_event_listener(MemoryMapEventListener * listener, void * client_data) {
Listener * l = NULL;
if (listener_cnt >= listener_max) {
listener_max += 8;
listeners = (Listener *)loc_realloc(listeners, listener_max * sizeof(Listener));
}
l = listeners + listener_cnt++;
l->listener = listener;
l->args = client_data;
}
static void write_map_region(OutputStream * out, MemoryRegion * m) {
MemoryRegionAttribute * x = m->attrs;
write_stream(out, '{');
json_write_string(out, "Addr");
write_stream(out, ':');
json_write_uint64(out, m->addr);
write_stream(out, ',');
json_write_string(out, "Size");
write_stream(out, ':');
json_write_uint64(out, m->size);
write_stream(out, ',');
json_write_string(out, "Flags");
write_stream(out, ':');
json_write_ulong(out, m->flags);
if (m->file_name != NULL) {
write_stream(out, ',');
json_write_string(out, "FileName");
write_stream(out, ':');
json_write_string(out, m->file_name);
write_stream(out, ',');
if (m->sect_name != NULL) {
json_write_string(out, "SectionName");
write_stream(out, ':');
json_write_string(out, m->sect_name);
}
else {
json_write_string(out, "Offs");
write_stream(out, ':');
json_write_uint64(out, m->file_offs);
}
if (m->bss) {
write_stream(out, ',');
json_write_string(out, "BSS");
write_stream(out, ':');
json_write_boolean(out, m->bss);
}
}
if (m->id != NULL) {
write_stream(out, ',');
json_write_string(out, "ID");
write_stream(out, ':');
json_write_string(out, m->id);
}
while (x != NULL) {
write_stream(out, ',');
json_write_string(out, x->name);
write_stream(out, ':');
write_string(out, x->value);
x = x->next;
}
write_stream(out, '}');
}
static void command_get(char * token, Channel * c) {
char id[256];
int err = 0;
Context * ctx = NULL;
MemoryMap * client_map = NULL;
MemoryMap * target_map = NULL;
json_read_string(&c->inp, id, sizeof(id));
if (read_stream(&c->inp) != 0) exception(ERR_JSON_SYNTAX);
if (read_stream(&c->inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
ctx = id2ctx(id);
if (ctx == NULL) err = ERR_INV_CONTEXT;
else ctx = context_get_group(ctx, CONTEXT_GROUP_PROCESS);
if (!err && memory_map_get(ctx, &client_map, &target_map) < 0) err = errno;
write_stringz(&c->out, "R");
write_stringz(&c->out, token);
write_errno(&c->out, err);
if (err) {
write_stringz(&c->out, "null");
}
else {
unsigned n;
unsigned cnt = 0;
write_stream(&c->out, '[');
for (n = 0; n < client_map->region_cnt; n++) {
if (cnt > 0) write_stream(&c->out, ',');
write_map_region(&c->out, client_map->regions + n);
cnt++;
}
for (n = 0; n < target_map->region_cnt; n++) {
if (cnt > 0) write_stream(&c->out, ',');
write_map_region(&c->out, target_map->regions + n);
cnt++;
}
write_stream(&c->out, ']');
write_stream(&c->out, 0);
}
write_stream(&c->out, MARKER_EOM);
}
static void read_map_attribute(InputStream * inp, const char * name, void * args) {
MemoryRegion * r = (MemoryRegion *)args;
if (strcmp(name, "Addr") == 0) r->addr = (ContextAddress)json_read_uint64(inp);
else if (strcmp(name, "Size") == 0) r->size = (ContextAddress)json_read_uint64(inp);
else if (strcmp(name, "Offs") == 0) r->file_offs = json_read_uint64(inp);
else if (strcmp(name, "BSS") == 0) r->bss = json_read_boolean(inp);
else if (strcmp(name, "Flags") == 0) r->flags = (unsigned)json_read_long(inp);
else if (strcmp(name, "FileName") == 0) r->file_name = json_read_alloc_string(inp);
else if (strcmp(name, "SectionName") == 0) r->sect_name = json_read_alloc_string(inp);
else if (strcmp(name, "ID") == 0) r->id = json_read_alloc_string(inp);
else {
MemoryRegionAttribute * x = (MemoryRegionAttribute *)loc_alloc(sizeof(MemoryRegionAttribute));
x->name = loc_strdup(name);
x->value = json_read_object(inp);
x->next = r->attrs;
r->attrs = x;
}
}
static void read_map_item(InputStream * inp, void * args) {
MemoryMap * map = (MemoryMap *)args;
MemoryRegion * r = add_region(map);
json_read_struct(inp, read_map_attribute, r);
}
static void command_set(char * token, Channel * c) {
char id[256];
unsigned h;
ClientMap * cm = NULL;
LINK * l = NULL;
MemoryMap map;
memset(&map, 0, sizeof(map));
json_read_string(&c->inp, id, sizeof(id));
if (read_stream(&c->inp) != 0) exception(ERR_JSON_SYNTAX);
json_read_array(&c->inp, read_map_item, &map);
if (read_stream(&c->inp) != 0) exception(ERR_JSON_SYNTAX);
if (read_stream(&c->inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
h = map_id_hash(id);
for (l = client_map_hash[h].next; l != &client_map_hash[h]; l = l->next) {
ClientMap * m = hash2map(l);
if (m->channel == c && strcmp(m->id, id) == 0) {
context_clear_memory_map(&m->map);
loc_free(m->map.regions);
cm = m;
break;
}
}
if (map.region_cnt > 0) {
if (cm == NULL) {
cm = (ClientMap *)loc_alloc_zero(sizeof(ClientMap));
cm->id = loc_strdup(id);
cm->channel = c;
list_add_last(&cm->link_list, &client_map_list);
list_add_last(&cm->link_hash, &client_map_hash[h]);
}
cm->map = map;
update_all_context_client_maps();
}
else if (cm != NULL) {
list_remove(&cm->link_list);
list_remove(&cm->link_hash);
loc_free(cm->id);
loc_free(cm);
update_all_context_client_maps();
}
write_stringz(&c->out, "R");
write_stringz(&c->out, token);
write_errno(&c->out, 0);
write_stream(&c->out, MARKER_EOM);
}
static void channel_close_listener(Channel * c) {
int notify = 0;
LINK * l = client_map_list.next;
while (l != &client_map_list) {
ClientMap * m = list2map(l);
l = l->next;
if (m->channel != c) continue;
list_remove(&m->link_list);
list_remove(&m->link_hash);
context_clear_memory_map(&m->map);
loc_free(m->map.regions);
loc_free(m->id);
loc_free(m);
notify = 1;
}
if (notify) update_all_context_client_maps();
}
void ini_memory_map_service(Protocol * proto, TCFBroadcastGroup * bcg) {
int i;
static ContextEventListener listener = {
event_memory_map_changed,
NULL,
NULL,
NULL,
event_memory_map_changed,
event_context_disposed
};
broadcast_group = bcg;
add_channel_close_listener(channel_close_listener);
list_init(&client_map_list);
for (i = 0; i < MAP_HASH_SIZE; i++) {
list_init(&client_map_hash[i]);
}
add_context_event_listener(&listener, NULL);
add_command_handler(proto, MEMORY_MAP, "get", command_get);
add_command_handler(proto, MEMORY_MAP, "set", command_set);
context_extension_offset = context_extension(sizeof(ContextExtensionMM));
}
#endif