Skip to main content
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to 'agent/tcf/services/stacktrace.c')
-rw-r--r--agent/tcf/services/stacktrace.c455
1 files changed, 455 insertions, 0 deletions
diff --git a/agent/tcf/services/stacktrace.c b/agent/tcf/services/stacktrace.c
new file mode 100644
index 00000000..927621fa
--- /dev/null
+++ b/agent/tcf/services/stacktrace.c
@@ -0,0 +1,455 @@
+/*******************************************************************************
+ * 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.
+ * You may elect to redistribute this code under either of these licenses.
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ *******************************************************************************/
+
+/*
+ * Target service implementation: stack trace (TCF name StackTrace)
+ */
+
+#include <config.h>
+
+#if SERVICE_StackTrace
+
+#include <stddef.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+#include <framework/myalloc.h>
+#include <framework/protocol.h>
+#include <framework/trace.h>
+#include <framework/context.h>
+#include <framework/json.h>
+#include <framework/cache.h>
+#include <framework/exceptions.h>
+#include <services/registers.h>
+#include <services/stacktrace.h>
+#include <services/symbols.h>
+
+#define MAX_FRAMES 1000
+
+static const char * STACKTRACE = "StackTrace";
+
+typedef struct StackTrace {
+ ErrorReport * error;
+ int valid;
+ int frame_cnt;
+ int frame_max;
+ StackFrame * frames; /* ordered bottom to top */
+} StackTrace;
+
+static size_t context_extension_offset = 0;
+
+#define EXT(ctx) ((StackTrace *)((char *)(ctx) + context_extension_offset))
+
+static void add_frame(StackTrace * stack, StackFrame * frame) {
+ if (stack->frame_cnt >= stack->frame_max) {
+ stack->frame_max += 32;
+ stack->frames = (StackFrame *)loc_realloc(stack->frames,
+ stack->frame_max * sizeof(StackFrame));
+ }
+ stack->frames[stack->frame_cnt++] = *frame;
+}
+
+static void invalidate_stack_trace(StackTrace * stack) {
+ int i;
+ release_error_report(stack->error);
+ for (i = 0; i < stack->frame_cnt; i++) {
+ loc_free(stack->frames[i].regs);
+ stack->frames[i].regs = NULL;
+ }
+ stack->error = NULL;
+ stack->frame_cnt = 0;
+ stack->valid = 0;
+}
+
+static void trace_stack(Context * ctx, StackTrace * stack) {
+ int i;
+ int error = 0;
+ StackFrame frame;
+ ContextAddress prev_fp = 0;
+
+ stack->frame_cnt = 0;
+ memset(&frame, 0, sizeof(frame));
+ frame.is_top_frame = 1;
+ frame.ctx = ctx;
+ trace(LOG_STACK, "Stack trace, ctx %s", ctx->id);
+ while (stack->frame_cnt < MAX_FRAMES) {
+ StackFrame down;
+ memset(&down, 0, sizeof(down));
+ down.ctx = ctx;
+ trace(LOG_STACK, "Frame %d", stack->frame_cnt);
+#if ENABLE_Trace
+ if (LOG_STACK & log_mode) {
+ uint64_t v;
+ RegisterDefinition * r;
+ for (r = get_reg_definitions(ctx); r->name != NULL; r++) {
+ if (read_reg_value(&frame, r, &v) == 0) {
+ trace(LOG_STACK, " %-8s %16"PRIX64, r->name, v);
+ }
+ }
+ }
+#endif
+#if ENABLE_Symbols
+ if (get_next_stack_frame(&frame, &down) < 0) {
+ error = errno;
+ trace(LOG_STACK, " trace error: %s", errno_to_str(errno));
+ loc_free(down.regs);
+ break;
+ }
+#endif
+ if (frame.fp == 0) {
+ trace(LOG_STACK, " *** frame info not available ***");
+ loc_free(down.regs);
+ memset(&down, 0, sizeof(down));
+ down.ctx = ctx;
+ if (crawl_stack_frame(&frame, &down) < 0) {
+ error = errno;
+ trace(LOG_STACK, " crawl error: %s", errno_to_str(errno));
+ loc_free(down.regs);
+ break;
+ }
+ }
+ trace(LOG_STACK, " cfa %16"PRIX64, (uint64_t)frame.fp);
+ if (stack->frame_cnt > 0 && frame.fp == prev_fp) {
+ loc_free(down.regs);
+ break;
+ }
+ add_frame(stack, &frame);
+ prev_fp = frame.fp;
+ frame = down;
+ }
+
+ if (frame.has_reg_data) add_frame(stack, &frame);
+ else loc_free(frame.regs);
+
+ if (get_error_code(error) == ERR_CACHE_MISS) {
+ invalidate_stack_trace(stack);
+ stack->error = get_error_report(ERR_CACHE_MISS);
+ }
+ else if (error && stack->frame_cnt == 0) {
+ stack->error = get_error_report(error);
+ }
+ for (i = 0; i < stack->frame_cnt / 2; i++) {
+ StackFrame f = stack->frames[i];
+ stack->frames[i] = stack->frames[stack->frame_cnt - i - 1];
+ stack->frames[stack->frame_cnt - i - 1] = f;
+ }
+}
+
+static StackTrace * create_stack_trace(Context * ctx) {
+ StackTrace * stack = EXT(ctx);
+ if (!stack->valid) {
+ assert(stack->frame_cnt == 0);
+ release_error_report(stack->error);
+ stack->error = NULL;
+ stack->valid = 1;
+ trace_stack(ctx, stack);
+ }
+ return stack;
+}
+
+static void write_context(OutputStream * out, char * id, Context * ctx, int level, StackFrame * frame, StackFrame * down) {
+ uint64_t v;
+ RegisterDefinition * reg_def = get_PC_definition(ctx);
+
+ write_stream(out, '{');
+
+ json_write_string(out, "ID");
+ write_stream(out, ':');
+ json_write_string(out, id);
+
+ write_stream(out, ',');
+ json_write_string(out, "ParentID");
+ write_stream(out, ':');
+ json_write_string(out, ctx->id);
+
+ write_stream(out, ',');
+ json_write_string(out, "Level");
+ write_stream(out, ':');
+ json_write_long(out, level);
+
+ write_stream(out, ',');
+ json_write_string(out, "ProcessID");
+ write_stream(out, ':');
+ json_write_string(out, context_get_group(ctx, CONTEXT_GROUP_PROCESS)->id);
+
+ if (frame->is_top_frame) {
+ write_stream(out, ',');
+ json_write_string(out, "TopFrame");
+ write_stream(out, ':');
+ json_write_boolean(out, 1);
+ }
+
+ if (frame->fp) {
+ write_stream(out, ',');
+ json_write_string(out, "FP");
+ write_stream(out, ':');
+ json_write_uint64(out, frame->fp);
+ }
+
+ if (read_reg_value(frame, reg_def, &v) == 0) {
+ write_stream(out, ',');
+ json_write_string(out, "IP");
+ write_stream(out, ':');
+ json_write_uint64(out, v);
+ }
+
+ if (down != NULL && read_reg_value(down, reg_def, &v) == 0) {
+ write_stream(out, ',');
+ json_write_string(out, "RP");
+ write_stream(out, ':');
+ json_write_uint64(out, v);
+ }
+
+ write_stream(out, '}');
+}
+
+typedef struct CommandGetContextData {
+ Context * ctx;
+ int frame;
+ StackFrame * info;
+ StackFrame * down;
+} CommandGetContextData;
+
+typedef struct CommandGetContextArgs {
+ char token[256];
+ int id_cnt;
+ char ** ids;
+ CommandGetContextData * data;
+} CommandGetContextArgs;
+
+static void command_get_context_cache_client(void * x) {
+ int i;
+ int err = 0;
+ CommandGetContextArgs * args = (CommandGetContextArgs *)x;
+ Channel * c = cache_channel();
+
+ memset(args->data, 0, sizeof(CommandGetContextData) * args->id_cnt);
+ for (i = 0; i < args->id_cnt; i++) {
+ StackTrace * stack = NULL;
+ CommandGetContextData * d = args->data + i;
+ if (id2frame(args->ids[i], &d->ctx, &d->frame) < 0) {
+ err = errno;
+ break;
+ }
+ if (!d->ctx->stopped) {
+ err = ERR_IS_RUNNING;
+ break;
+ }
+ stack = create_stack_trace(d->ctx);
+ if (stack->error) {
+ err = set_error_report_errno(stack->error);
+ break;
+ }
+ if (d->frame >= stack->frame_cnt) {
+ err = ERR_INV_CONTEXT;
+ break;
+ }
+ d->info = stack->frames + d->frame;
+ d->down = d->frame > 0 ? d->info - 1 : NULL;
+ }
+
+ cache_exit();
+
+ write_stringz(&c->out, "R");
+ write_stringz(&c->out, args->token);
+ write_stream(&c->out, '[');
+ for (i = 0; i < args->id_cnt; i++) {
+ CommandGetContextData * d = args->data + i;
+ if (i > 0) write_stream(&c->out, ',');
+ if (d->info == NULL) {
+ write_string(&c->out, "null");
+ }
+ else {
+ write_context(&c->out, args->ids[i], d->ctx, d->frame, d->info, d->down);
+ }
+ }
+ write_stream(&c->out, ']');
+ write_stream(&c->out, 0);
+ write_errno(&c->out, err);
+ write_stream(&c->out, MARKER_EOM);
+
+ loc_free(args->ids);
+ loc_free(args->data);
+}
+
+static void command_get_context(char * token, Channel * c) {
+ CommandGetContextArgs args;
+
+ args.ids = json_read_alloc_string_array(&c->inp, &args.id_cnt);
+ if (read_stream(&c->inp) != 0) exception(ERR_JSON_SYNTAX);
+ if (read_stream(&c->inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
+
+ args.data = (CommandGetContextData *)loc_alloc(sizeof(CommandGetContextData) * args.id_cnt);
+ strlcpy(args.token, token, sizeof(args.token));
+ cache_enter(command_get_context_cache_client, c, &args, sizeof(args));
+}
+
+typedef struct CommandGetChildrenArgs {
+ char token[256];
+ char id[256];
+} CommandGetChildrenArgs;
+
+static void command_get_children_cache_client(void * x) {
+ int err = 0;
+ Context * ctx = NULL;
+ StackTrace * stack = NULL;
+ CommandGetChildrenArgs * args = (CommandGetChildrenArgs *)x;
+ Channel * c = cache_channel();
+
+ ctx = id2ctx(args->id);
+ if (ctx == NULL || !context_has_state(ctx)) {
+ /* no children */
+ }
+ else if (!ctx->stopped) {
+ err = ERR_IS_RUNNING;
+ }
+ else {
+ stack = create_stack_trace(ctx);
+ }
+
+ cache_exit();
+
+ write_stringz(&c->out, "R");
+ write_stringz(&c->out, args->token);
+
+ write_errno(&c->out, stack != NULL ? set_error_report_errno(stack->error) : err);
+
+ if (stack == NULL) {
+ write_stringz(&c->out, "null");
+ }
+ else {
+ int i;
+ write_stream(&c->out, '[');
+ for (i = 0; i < stack->frame_cnt; i++) {
+ if (i > 0) write_stream(&c->out, ',');
+ json_write_string(&c->out, frame2id(ctx, i));
+ }
+ write_stream(&c->out, ']');
+ write_stream(&c->out, 0);
+ }
+
+ write_stream(&c->out, MARKER_EOM);
+}
+
+static void command_get_children(char * token, Channel * c) {
+ CommandGetChildrenArgs args;
+
+ json_read_string(&c->inp, args.id, sizeof(args.id));
+ if (read_stream(&c->inp) != 0) exception(ERR_JSON_SYNTAX);
+ if (read_stream(&c->inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
+
+ strlcpy(args.token, token, sizeof(args.token));
+ cache_enter(command_get_children_cache_client, c, &args, sizeof(args));
+}
+
+int get_top_frame(Context * ctx) {
+ StackTrace * stack;
+
+ if (!ctx->stopped) {
+ errno = ERR_IS_RUNNING;
+ return STACK_TOP_FRAME;
+ }
+
+ stack = create_stack_trace(ctx);
+ if (stack->error != NULL) {
+ set_error_report_errno(stack->error);
+ return STACK_TOP_FRAME;
+ }
+
+ assert(stack->frame_cnt > 0);
+ return stack->frame_cnt - 1;
+}
+
+int get_frame_info(Context * ctx, int frame, StackFrame ** info) {
+ StackTrace * stack;
+
+ *info = NULL;
+ if (ctx == NULL || !context_has_state(ctx)) {
+ errno = ERR_INV_CONTEXT;
+ return -1;
+ }
+ if (!ctx->stopped) {
+ errno = ERR_IS_RUNNING;
+ return -1;
+ }
+
+ stack = create_stack_trace(ctx);
+ if (stack->error != NULL) {
+ set_error_report_errno(stack->error);
+ return -1;
+ }
+
+ if (frame == STACK_TOP_FRAME) {
+ if (stack->frame_cnt == 0) {
+ errno = set_errno(ERR_INV_CONTEXT, "No such stack frame");
+ return -1;
+ }
+ frame = stack->frame_cnt - 1;
+ }
+ else if (frame < 0 || frame >= stack->frame_cnt) {
+ errno = set_errno(ERR_INV_CONTEXT, "No such stack frame");
+ return -1;
+ }
+
+ *info = stack->frames + frame;
+ return 0;
+}
+
+int is_top_frame(Context * ctx, int frame) {
+ StackTrace * stack;
+
+ if (ctx == NULL || !context_has_state(ctx)) return 0;
+ if (frame == STACK_TOP_FRAME) return 1;
+ if (!ctx->stopped) return 0;
+ stack = create_stack_trace(ctx);
+ if (stack->error != NULL) return 0;
+ return frame == stack->frame_cnt - 1;
+}
+
+static void flush_stack_trace(Context * ctx, void * args) {
+ invalidate_stack_trace(EXT(ctx));
+}
+
+static void flush_on_register_change(Context * ctx, int frame, RegisterDefinition * def, void * args) {
+ invalidate_stack_trace(EXT(ctx));
+}
+
+static void delete_stack_trace(Context * ctx, void * args) {
+ invalidate_stack_trace(EXT(ctx));
+ loc_free(EXT(ctx)->frames);
+ memset(EXT(ctx), 0, sizeof(StackTrace));
+}
+
+void ini_stack_trace_service(Protocol * proto, TCFBroadcastGroup * bcg) {
+ static ContextEventListener context_listener = {
+ NULL,
+ flush_stack_trace,
+ NULL,
+ flush_stack_trace,
+ flush_stack_trace,
+ delete_stack_trace
+ };
+ static RegistersEventListener registers_listener = {
+ flush_on_register_change,
+ };
+ add_context_event_listener(&context_listener, bcg);
+ add_registers_event_listener(&registers_listener, bcg);
+ add_command_handler(proto, STACKTRACE, "getContext", command_get_context);
+ add_command_handler(proto, STACKTRACE, "getChildren", command_get_children);
+ context_extension_offset = context_extension(sizeof(StackTrace));
+}
+
+#endif

Back to the top