| /******************************************************************************* |
| * Copyright (c) 2007, 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 handles process/thread OS contexts and their state machine. |
| */ |
| |
| #include <config.h> |
| |
| #include <assert.h> |
| #include <framework/context.h> |
| #include <framework/myalloc.h> |
| |
| typedef struct Listener { |
| ContextEventListener * func; |
| void * args; |
| } Listener; |
| |
| static Listener * listeners = NULL; |
| static unsigned listener_cnt = 0; |
| static unsigned listener_max = 0; |
| |
| LINK context_root = { NULL, NULL }; |
| |
| const char * REASON_USER_REQUEST = "Suspended"; |
| const char * REASON_STEP = "Step"; |
| const char * REASON_BREAKPOINT = "Breakpoint"; |
| const char * REASON_EXCEPTION = "Exception"; |
| const char * REASON_CONTAINER = "Container"; |
| const char * REASON_WATCHPOINT = "Watchpoint"; |
| const char * REASON_SIGNAL = "Signal"; |
| const char * REASON_SHAREDLIB = "Shared Library"; |
| const char * REASON_ERROR = "Error"; |
| |
| char * pid2id(pid_t pid, pid_t parent) { |
| static char s[64]; |
| char * p = s + sizeof(s); |
| unsigned long n = (long)pid; |
| *(--p) = 0; |
| do { |
| *(--p) = (char)(n % 10 + '0'); |
| n = n / 10; |
| } |
| while (n != 0); |
| if (parent != 0) { |
| n = (long)parent; |
| *(--p) = '.'; |
| do { |
| *(--p) = (char)(n % 10 + '0'); |
| n = n / 10; |
| } |
| while (n != 0); |
| } |
| *(--p) = 'P'; |
| return p; |
| } |
| |
| pid_t id2pid(const char * id, pid_t * parent) { |
| /* TODO: (pid_t)0 is valid value in Windows, should use (pid_t)-1 to indicate an error */ |
| pid_t pid = 0; |
| if (parent != NULL) *parent = 0; |
| if (id == NULL) return 0; |
| if (id[0] != 'P') return 0; |
| if (id[1] == 0) return 0; |
| pid = (pid_t)strtol(id + 1, (char **)&id, 10); |
| if (id[0] == '.') { |
| if (id[1] == 0) return 0; |
| if (parent != NULL) *parent = pid; |
| pid = (pid_t)strtol(id + 1, (char **)&id, 10); |
| } |
| if (id[0] != 0) return 0; |
| return pid; |
| } |
| |
| void add_context_event_listener(ContextEventListener * listener, void * client_data) { |
| if (listener_cnt >= listener_max) { |
| listener_max += 8; |
| listeners = (Listener *)loc_realloc(listeners, listener_max * sizeof(Listener)); |
| } |
| listeners[listener_cnt].func = listener; |
| listeners[listener_cnt].args = client_data; |
| listener_cnt++; |
| } |
| |
| #if ENABLE_DebugContext |
| |
| static size_t extension_size = 0; |
| static int context_created = 0; |
| |
| size_t context_extension(size_t size) { |
| size_t offs = 0; |
| assert(!context_created); |
| while (extension_size % sizeof(void *) != 0) extension_size++; |
| offs = sizeof(Context) + extension_size; |
| extension_size += size; |
| return offs; |
| } |
| |
| Context * create_context(const char * id) { |
| Context * ctx = (Context *)loc_alloc_zero(sizeof(Context) + extension_size); |
| |
| strlcpy(ctx->id, id, sizeof(ctx->id)); |
| list_init(&ctx->children); |
| context_created = 1; |
| return ctx; |
| } |
| |
| void context_lock(Context * ctx) { |
| assert(ctx->ref_count > 0); |
| ctx->ref_count++; |
| } |
| |
| void context_unlock(Context * ctx) { |
| assert(ctx->ref_count > 0); |
| if (--(ctx->ref_count) == 0) { |
| unsigned i; |
| |
| assert(ctx->exited); |
| assert(list_is_empty(&ctx->children)); |
| if (ctx->parent != NULL) { |
| list_remove(&ctx->cldl); |
| context_unlock(ctx->parent); |
| ctx->parent = NULL; |
| } |
| if (ctx->creator != NULL) { |
| context_unlock(ctx->creator); |
| ctx->creator = NULL; |
| } |
| |
| assert(!ctx->event_notification); |
| ctx->event_notification = 1; |
| for (i = 0; i < listener_cnt; i++) { |
| Listener * l = listeners + i; |
| if (l->func->context_disposed == NULL) continue; |
| l->func->context_disposed(ctx, l->args); |
| } |
| ctx->event_notification = 0; |
| list_remove(&ctx->ctxl); |
| loc_free(ctx->name); |
| loc_free(ctx); |
| } |
| } |
| |
| const char * context_state_name(Context * ctx) { |
| if (ctx->exited) return "exited"; |
| if (ctx->stopped) return "stopped"; |
| return "running"; |
| } |
| |
| #if !ENABLE_ContextStateProperties |
| int context_get_state_properties(Context * ctx, const char *** names, const char *** values, int * cnt) { |
| *cnt = 0; |
| return 0; |
| } |
| #endif |
| |
| void context_clear_memory_map(MemoryMap * map) { |
| unsigned i; |
| for (i = 0; i < map->region_cnt; i++) { |
| MemoryRegion * r = map->regions + i; |
| loc_free(r->file_name); |
| loc_free(r->sect_name); |
| loc_free(r->id); |
| while (r->attrs != NULL) { |
| MemoryRegionAttribute * x = r->attrs; |
| r->attrs = x->next; |
| loc_free(x->name); |
| loc_free(x->value); |
| loc_free(x); |
| } |
| } |
| memset(map->regions, 0, sizeof(MemoryRegion) * map->region_max); |
| map->region_cnt = 0; |
| } |
| |
| void send_context_created_event(Context * ctx) { |
| unsigned i; |
| assert(ctx->ref_count > 0); |
| assert(!ctx->event_notification); |
| ctx->event_notification = 1; |
| for (i = 0; i < listener_cnt; i++) { |
| Listener * l = listeners + i; |
| if (l->func->context_created == NULL) continue; |
| l->func->context_created(ctx, l->args); |
| } |
| ctx->event_notification = 0; |
| } |
| |
| void send_context_changed_event(Context * ctx) { |
| unsigned i; |
| assert(ctx->ref_count > 0); |
| assert(!ctx->event_notification); |
| ctx->event_notification = 1; |
| for (i = 0; i < listener_cnt; i++) { |
| Listener * l = listeners + i; |
| if (l->func->context_changed == NULL) continue; |
| l->func->context_changed(ctx, l->args); |
| } |
| ctx->event_notification = 0; |
| } |
| |
| void send_context_stopped_event(Context * ctx) { |
| unsigned i; |
| assert(ctx->ref_count > 0); |
| assert(ctx->stopped != 0); |
| assert(!ctx->event_notification); |
| assert(context_has_state(ctx)); |
| ctx->event_notification = 1; |
| for (i = 0; i < listener_cnt; i++) { |
| Listener * l = listeners + i; |
| if (l->func->context_stopped == NULL) continue; |
| l->func->context_stopped(ctx, l->args); |
| assert(ctx->stopped != 0); |
| } |
| ctx->event_notification = 0; |
| } |
| |
| void send_context_started_event(Context * ctx) { |
| unsigned i; |
| assert(ctx->ref_count > 0); |
| assert(context_has_state(ctx)); |
| ctx->stopped = 0; |
| ctx->stopped_by_bp = 0; |
| ctx->stopped_by_cb = NULL; |
| ctx->stopped_by_exception = 0; |
| if (ctx->exception_description) { |
| loc_free(ctx->exception_description); |
| ctx->exception_description = NULL; |
| } |
| ctx->event_notification++; |
| for (i = 0; i < listener_cnt; i++) { |
| Listener * l = listeners + i; |
| if (l->func->context_started == NULL) continue; |
| l->func->context_started(ctx, l->args); |
| } |
| ctx->event_notification--; |
| } |
| |
| void send_context_exited_event(Context * ctx) { |
| unsigned i; |
| assert(!ctx->event_notification); |
| assert(!ctx->exited); |
| ctx->exiting = 0; |
| ctx->pending_intercept = 0; |
| ctx->exited = 1; |
| ctx->event_notification = 1; |
| for (i = 0; i < listener_cnt; i++) { |
| Listener * l = listeners + i; |
| if (l->func->context_exited == NULL) continue; |
| l->func->context_exited(ctx, l->args); |
| } |
| ctx->event_notification = 0; |
| context_unlock(ctx); |
| } |
| |
| void ini_contexts(void) { |
| list_init(&context_root); |
| init_contexts_sys_dep(); |
| } |
| |
| #endif /* if ENABLE_DebugContext */ |