| /******************************************************************************* |
| * Copyright (c) 2007, 2013 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 |
| *******************************************************************************/ |
| |
| /* |
| * This module handles process/thread OS contexts and their state machine. |
| */ |
| |
| #include <tcf/config.h> |
| |
| #include <assert.h> |
| #include <tcf/framework/context.h> |
| #include <tcf/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; |
| |
| static size_t extension_size = 0; |
| static int context_created = 0; |
| |
| LINK context_root = TCF_LIST_INIT(context_root); |
| |
| const char * REASON_USER_REQUEST = "Suspended"; |
| const char * REASON_STEP = "Step"; |
| const char * REASON_ACTIVE = "Active"; |
| 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++ != 'P') return 0; |
| while (*id >= '0' && *id <= '9') { |
| pid = pid * 10 + (*id++ - '0'); |
| } |
| if (*id == '.') { |
| if (parent != NULL) *parent = pid; |
| id++; |
| pid = 0; |
| while (*id >= '0' && *id <= '9') { |
| pid = pid * 10 + (*id++ - '0'); |
| } |
| } |
| if (*id != 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++; |
| } |
| |
| size_t context_extension(size_t size) { |
| size_t offs; |
| 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_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->query); |
| 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; |
| } |
| |
| #if ENABLE_DebugContext |
| |
| static char * buf = NULL; |
| static size_t buf_pos = 0; |
| static size_t buf_max = 0; |
| |
| static void buf_char(char ch) { |
| if (buf_pos >= buf_max) { |
| buf_max += 0x100; |
| buf = (char *)loc_realloc(buf, buf_max); |
| } |
| buf[buf_pos++] = ch; |
| } |
| |
| static void get_context_full_name(Context * ctx) { |
| if (ctx != NULL) { |
| char * name = ctx->name; |
| get_context_full_name(ctx->parent); |
| buf_char('/'); |
| if (name != NULL) { |
| int quote = strchr(name, '/') != NULL; |
| if (quote) buf_char('"'); |
| while (*name) buf_char(*name++); |
| if (quote) buf_char('"'); |
| } |
| } |
| } |
| |
| const char * context_full_name(Context * ctx) { |
| buf_pos = 0; |
| get_context_full_name(ctx); |
| buf_char(0); |
| return buf; |
| } |
| |
| 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); |
| sigset_clear(&ctx->pending_signals); |
| sigset_clear(&ctx->sig_dont_stop); |
| sigset_clear(&ctx->sig_dont_pass); |
| 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"; |
| } |
| |
| 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; |
| ctx->stopped_by_funccall = 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) { |
| ini_cpudefs(); |
| init_contexts_sys_dep(); |
| } |
| |
| #endif /* if ENABLE_DebugContext */ |