Skip to main content
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEugene Tarassov2011-11-10 20:19:02 -0500
committerEugene Tarassov2011-11-10 20:19:02 -0500
commita107edc216f94c5ec059fdf74541908bc7e979bb (patch)
treea0d9a448043cf5b969c2ede059b7d19a3aa64b71 /agent/tcf/services
parent07c7a005cded9e4b4ec842d756d0e4d79dade149 (diff)
downloadorg.eclipse.tcf.agent-a107edc216f94c5ec059fdf74541908bc7e979bb.tar.gz
org.eclipse.tcf.agent-a107edc216f94c5ec059fdf74541908bc7e979bb.tar.xz
org.eclipse.tcf.agent-a107edc216f94c5ec059fdf74541908bc7e979bb.zip
Agent code is moved into separate "agent" directory, all C code moved into "tcf" directory.
Diffstat (limited to 'agent/tcf/services')
-rw-r--r--agent/tcf/services/breakpoints.c2573
-rw-r--r--agent/tcf/services/breakpoints.h203
-rw-r--r--agent/tcf/services/diagnostics.c451
-rw-r--r--agent/tcf/services/diagnostics.h31
-rw-r--r--agent/tcf/services/discovery.c202
-rw-r--r--agent/tcf/services/discovery.h50
-rw-r--r--agent/tcf/services/discovery_udp.c879
-rw-r--r--agent/tcf/services/discovery_udp.h52
-rw-r--r--agent/tcf/services/dwarf.h574
-rw-r--r--agent/tcf/services/dwarfcache.c1345
-rw-r--r--agent/tcf/services/dwarfcache.h273
-rw-r--r--agent/tcf/services/dwarfexpr.c333
-rw-r--r--agent/tcf/services/dwarfexpr.h34
-rw-r--r--agent/tcf/services/dwarfframe.c1019
-rw-r--r--agent/tcf/services/dwarfframe.h56
-rw-r--r--agent/tcf/services/dwarfio.c764
-rw-r--r--agent/tcf/services/dwarfio.h107
-rw-r--r--agent/tcf/services/dwarfreloc.c236
-rw-r--r--agent/tcf/services/dwarfreloc.h35
-rw-r--r--agent/tcf/services/expressions.c2809
-rw-r--r--agent/tcf/services/expressions.h92
-rw-r--r--agent/tcf/services/filesystem.c1397
-rw-r--r--agent/tcf/services/filesystem.h29
-rw-r--r--agent/tcf/services/linenumbers.c267
-rw-r--r--agent/tcf/services/linenumbers.h59
-rw-r--r--agent/tcf/services/linenumbers_elf.c305
-rw-r--r--agent/tcf/services/linenumbers_proxy.c316
-rw-r--r--agent/tcf/services/linenumbers_win32.c207
-rw-r--r--agent/tcf/services/memorymap.c543
-rw-r--r--agent/tcf/services/memorymap.h70
-rw-r--r--agent/tcf/services/memoryservice.c700
-rw-r--r--agent/tcf/services/memoryservice.h34
-rw-r--r--agent/tcf/services/pathmap.c510
-rw-r--r--agent/tcf/services/pathmap.h125
-rw-r--r--agent/tcf/services/processes.c1495
-rw-r--r--agent/tcf/services/processes.h60
-rw-r--r--agent/tcf/services/registers.c664
-rw-r--r--agent/tcf/services/registers.h51
-rw-r--r--agent/tcf/services/runctrl.c1749
-rw-r--r--agent/tcf/services/runctrl.h139
-rw-r--r--agent/tcf/services/stacktrace.c455
-rw-r--r--agent/tcf/services/stacktrace.h54
-rw-r--r--agent/tcf/services/streamsservice.c856
-rw-r--r--agent/tcf/services/streamsservice.h52
-rw-r--r--agent/tcf/services/symbols.c689
-rw-r--r--agent/tcf/services/symbols.h227
-rw-r--r--agent/tcf/services/symbols_alloc.h76
-rw-r--r--agent/tcf/services/symbols_elf.c2159
-rw-r--r--agent/tcf/services/symbols_proxy.c1356
-rw-r--r--agent/tcf/services/symbols_win32.c1213
-rw-r--r--agent/tcf/services/sysmon.c1634
-rw-r--r--agent/tcf/services/sysmon.h30
-rw-r--r--agent/tcf/services/tcf_elf.c1454
-rw-r--r--agent/tcf/services/tcf_elf.h594
-rw-r--r--agent/tcf/services/terminals.c584
-rw-r--r--agent/tcf/services/terminals.h29
-rw-r--r--agent/tcf/services/vm.c596
-rw-r--r--agent/tcf/services/vm.h58
58 files changed, 32954 insertions, 0 deletions
diff --git a/agent/tcf/services/breakpoints.c b/agent/tcf/services/breakpoints.c
new file mode 100644
index 00000000..28cde4fd
--- /dev/null
+++ b/agent/tcf/services/breakpoints.c
@@ -0,0 +1,2573 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2011 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 implements Breakpoints service.
+ * The service maintains a bp_arr of breakpoints.
+ * Each breakpoint consists of one or more conditions that determine
+ * when a program's execution should be interrupted.
+ */
+
+#include <config.h>
+
+#if SERVICE_Breakpoints
+
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <framework/channel.h>
+#include <framework/protocol.h>
+#include <framework/errors.h>
+#include <framework/trace.h>
+#include <framework/context.h>
+#include <framework/myalloc.h>
+#include <framework/exceptions.h>
+#include <framework/cache.h>
+#include <framework/json.h>
+#include <framework/link.h>
+#include <services/symbols.h>
+#include <services/runctrl.h>
+#include <services/breakpoints.h>
+#include <services/expressions.h>
+#include <services/linenumbers.h>
+#include <services/stacktrace.h>
+#include <services/memorymap.h>
+#include <services/pathmap.h>
+
+typedef struct BreakpointRef BreakpointRef;
+typedef struct InstructionRef InstructionRef;
+typedef struct BreakInstruction BreakInstruction;
+typedef struct EvaluationArgs EvaluationArgs;
+typedef struct EvaluationRequest EvaluationRequest;
+typedef struct ConditionEvaluationRequest ConditionEvaluationRequest;
+typedef struct ContextExtensionBP ContextExtensionBP;
+
+struct BreakpointRef {
+ LINK link_inp;
+ LINK link_bp;
+ Channel * channel; /* NULL means API client */
+ BreakpointInfo * bp;
+};
+
+struct BreakpointInfo {
+ Context * ctx; /* NULL means all contexts */
+ LINK link_all;
+ LINK link_id;
+ LINK link_clients;
+ char id[256];
+ int enabled;
+ int client_cnt;
+ int instruction_cnt;
+ ErrorReport * error;
+ char * location;
+ char * condition;
+ char ** context_ids;
+ char ** context_ids_prev;
+ char ** context_names;
+ char ** context_names_prev;
+ char ** stop_group;
+ char * file;
+ char * client_data;
+ int access_mode;
+ int access_size;
+ int line;
+ int column;
+ int ignore_count;
+ int hit_count;
+ BreakpointAttribute * attrs;
+ BreakpointAttribute * unsupported;
+
+ EventPointCallBack * event_callback;
+ void * event_callback_args;
+
+ int status_changed;
+};
+
+struct InstructionRef {
+ BreakpointInfo * bp;
+ Context * ctx;
+ ContextAddress addr;
+ ContextAddress size;
+ ErrorReport * address_error;
+ int cnt;
+};
+
+struct BreakInstruction {
+ LINK link_all;
+ LINK link_adr;
+ int virtual_addr;
+ ContextBreakpoint cb;
+ char saved_code[16];
+ size_t saved_size;
+ ErrorReport * planting_error;
+ int stepping_over_bp;
+ InstructionRef * refs;
+ int ref_size;
+ int ref_cnt;
+ int valid;
+ int planted;
+};
+
+struct EvaluationArgs {
+ BreakpointInfo * bp;
+ Context * ctx;
+};
+
+struct ConditionEvaluationRequest {
+ BreakpointInfo * bp;
+ int condition_ok;
+ int triggered;
+};
+
+struct EvaluationRequest {
+ Context * ctx;
+ BreakpointInfo * bp; /* NULL means all breakpoints */
+ LINK link_posted;
+ LINK link_active;
+ int location;
+ int bp_cnt;
+ int bp_max;
+ ConditionEvaluationRequest * bp_arr;
+};
+
+struct ContextExtensionBP {
+ int step_over_bp_cnt;
+ BreakInstruction * stepping_over_bp; /* if not NULL, the context is stepping over a breakpoint instruction */
+ char ** bp_ids; /* if stopped by breakpoint, contains NULL-terminated list of breakpoint IDs */
+ EvaluationRequest * req;
+ Context * bp_grp;
+ int empty_bp_grp;
+};
+
+static const char * BREAKPOINTS = "Breakpoints";
+
+static size_t context_extension_offset = 0;
+
+typedef struct Listener {
+ BreakpointsEventListener * listener;
+ void * args;
+} Listener;
+
+static Listener * listeners = NULL;
+static unsigned listener_cnt = 0;
+static unsigned listener_max = 0;
+
+#define EXT(ctx) ((ContextExtensionBP *)((char *)(ctx) + context_extension_offset))
+
+#define is_disabled(bp) (bp->enabled == 0 || bp->client_cnt == 0 || bp->unsupported != NULL)
+
+#define ADDR2INSTR_HASH_SIZE (32 * MEM_USAGE_FACTOR - 1)
+#define addr2instr_hash(ctx, addr) ((unsigned)((uintptr_t)(ctx) + (uintptr_t)(addr) + ((uintptr_t)(addr) >> 8)) % ADDR2INSTR_HASH_SIZE)
+
+#define link_all2bi(A) ((BreakInstruction *)((char *)(A) - offsetof(BreakInstruction, link_all)))
+#define link_adr2bi(A) ((BreakInstruction *)((char *)(A) - offsetof(BreakInstruction, link_adr)))
+
+#define ID2BP_HASH_SIZE (32 * MEM_USAGE_FACTOR - 1)
+
+#define link_all2bp(A) ((BreakpointInfo *)((char *)(A) - offsetof(BreakpointInfo, link_all)))
+#define link_id2bp(A) ((BreakpointInfo *)((char *)(A) - offsetof(BreakpointInfo, link_id)))
+
+#define INP2BR_HASH_SIZE (4 * MEM_USAGE_FACTOR - 1)
+
+#define link_inp2br(A) ((BreakpointRef *)((char *)(A) - offsetof(BreakpointRef, link_inp)))
+#define link_bp2br(A) ((BreakpointRef *)((char *)(A) - offsetof(BreakpointRef, link_bp)))
+
+#define link_posted2erl(A) ((EvaluationRequest *)((char *)(A) - offsetof(EvaluationRequest, link_posted)))
+#define link_active2erl(A) ((EvaluationRequest *)((char *)(A) - offsetof(EvaluationRequest, link_active)))
+#define link_bcg2chnl(A) ((Channel *)((char *)(A) - offsetof(Channel, bclink)))
+
+static LINK breakpoints;
+static LINK id2bp[ID2BP_HASH_SIZE];
+
+static LINK instructions;
+static LINK addr2instr[ADDR2INSTR_HASH_SIZE];
+
+static LINK inp2br[INP2BR_HASH_SIZE];
+
+static LINK evaluations_posted;
+static LINK evaluations_active;
+static uintptr_t generation_posted = 0;
+static uintptr_t generation_active = 0;
+static uintptr_t generation_done = 0;
+static int planting_instruction = 0;
+static int cache_enter_cnt = 0;
+
+static TCFBroadcastGroup * broadcast_group = NULL;
+
+static unsigned id2bp_hash(char * id) {
+ unsigned hash = 0;
+ while (*id) hash = (hash >> 16) + hash + (unsigned char)*id++;
+ return hash % ID2BP_HASH_SIZE;
+}
+
+static void get_bi_access_types(BreakInstruction * bi, unsigned * access_types, ContextAddress * access_size) {
+ int i;
+ unsigned t = 0;
+ ContextAddress sz = 0;
+ if (bi->virtual_addr) t |= CTX_BP_ACCESS_VIRTUAL;
+ for (i = 0; i < bi->ref_cnt; i++) {
+ if (bi->refs[i].cnt) {
+ int md = bi->refs[i].bp->access_mode;
+ if (md == 0) {
+ t |= CTX_BP_ACCESS_INSTRUCTION;
+ }
+ else {
+ t |= md;
+ }
+ if (sz < bi->refs[i].size) sz = bi->refs[i].size;
+ /* TODO: parse type (soft|hw) */
+ }
+ }
+ *access_types = t;
+ *access_size = sz;
+}
+
+static void plant_instruction(BreakInstruction * bi) {
+ int i;
+ int error = 0;
+ size_t saved_size = bi->saved_size;
+ ErrorReport * rp = NULL;
+
+ assert(!bi->stepping_over_bp);
+ assert(!bi->planted);
+ assert(!bi->cb.ctx->exited);
+ assert(bi->valid || bi->virtual_addr);
+ if (bi->cb.address == 0) return;
+ assert(is_all_stopped(bi->cb.ctx));
+
+ get_bi_access_types(bi, &bi->cb.access_types, &bi->cb.length);
+
+ bi->saved_size = 0;
+ if (context_plant_breakpoint(&bi->cb) < 0) {
+ if (bi->cb.access_types == CTX_BP_ACCESS_INSTRUCTION && get_error_code(errno) == ERR_UNSUPPORTED) {
+ uint8_t * break_inst = get_break_instruction(bi->cb.ctx, &bi->saved_size);
+ assert(sizeof(bi->saved_code) >= bi->saved_size);
+ planting_instruction = 1;
+ if (context_read_mem(bi->cb.ctx, bi->cb.address, bi->saved_code, bi->saved_size) < 0) {
+ error = errno;
+ }
+ else if (context_write_mem(bi->cb.ctx, bi->cb.address, break_inst, bi->saved_size) < 0) {
+ error = errno;
+ }
+ planting_instruction = 0;
+ }
+ else {
+ error = errno;
+ }
+ }
+ rp = get_error_report(error);
+ if (saved_size != bi->saved_size || !compare_error_reports(bi->planting_error, rp)) {
+ release_error_report(bi->planting_error);
+ bi->planting_error = rp;
+ for (i = 0; i < bi->ref_cnt; i++) {
+ bi->refs[i].bp->status_changed = 1;
+ }
+ }
+ else {
+ release_error_report(rp);
+ }
+ bi->planted = bi->planting_error == NULL;
+}
+
+static void remove_instruction(BreakInstruction * bi) {
+ assert(bi->planted);
+ assert(bi->planting_error == NULL);
+ assert(is_all_stopped(bi->cb.ctx));
+ if (bi->saved_size) {
+ if (!bi->cb.ctx->exited) {
+ planting_instruction = 1;
+ if (context_write_mem(bi->cb.ctx, bi->cb.address, bi->saved_code, bi->saved_size) < 0) {
+ bi->planting_error = get_error_report(errno);
+ }
+ planting_instruction = 0;
+ }
+ }
+ else if (context_unplant_breakpoint(&bi->cb) < 0) {
+ bi->planting_error = get_error_report(errno);
+ }
+ bi->planted = 0;
+}
+
+#ifndef NDEBUG
+static int is_canonical_addr(Context * ctx, ContextAddress address) {
+ Context * mem = NULL;
+ ContextAddress mem_addr = 0;
+ if (context_get_canonical_addr(ctx, address, &mem, &mem_addr, NULL, NULL) < 0) return 0;
+ return mem == ctx && address == mem_addr;
+}
+#endif
+
+static BreakInstruction * find_instruction(Context * ctx, int virtual_addr, ContextAddress address) {
+ int hash = addr2instr_hash(ctx, address);
+ LINK * l = addr2instr[hash].next;
+ if (address == 0) return NULL;
+ assert(virtual_addr || is_canonical_addr(ctx, address));
+ while (l != addr2instr + hash) {
+ BreakInstruction * bi = link_adr2bi(l);
+ if (bi->cb.ctx == ctx &&
+ bi->cb.address == address &&
+ bi->virtual_addr == virtual_addr) return bi;
+ l = l->next;
+ }
+ return NULL;
+}
+
+static BreakInstruction * add_instruction(Context * ctx, int virtual_addr, ContextAddress address) {
+ int hash = addr2instr_hash(ctx, address);
+ BreakInstruction * bi = (BreakInstruction *)loc_alloc_zero(sizeof(BreakInstruction));
+ assert(find_instruction(ctx, virtual_addr, address) == NULL);
+ list_add_last(&bi->link_all, &instructions);
+ list_add_last(&bi->link_adr, addr2instr + hash);
+ context_lock(ctx);
+ bi->cb.ctx = ctx;
+ bi->cb.address = address;
+ bi->virtual_addr = virtual_addr;
+ return bi;
+}
+
+static void clear_instruction_refs(Context * ctx, BreakpointInfo * bp) {
+ LINK * l = instructions.next;
+ while (l != &instructions) {
+ int i;
+ BreakInstruction * bi = link_all2bi(l);
+ for (i = 0; i < bi->ref_cnt; i++) {
+ InstructionRef * ref = bi->refs + i;
+ if (ref->ctx != ctx) continue;
+ if (bp != NULL && ref->bp != bp) continue;
+ ref->size = 0;
+ ref->cnt = 0;
+ bi->valid = 0;
+ }
+ l = l->next;
+ }
+}
+
+static void flush_instructions(void) {
+ LINK * l = instructions.next;
+ while (l != &instructions) {
+ int i = 0;
+ BreakInstruction * bi = link_all2bi(l);
+ l = l->next;
+ if (bi->valid) continue;
+ while (i < bi->ref_cnt) {
+ if (bi->refs[i].cnt == 0) {
+ bi->refs[i].bp->instruction_cnt--;
+ bi->refs[i].bp->status_changed = 1;
+ context_unlock(bi->refs[i].ctx);
+ release_error_report(bi->refs[i].address_error);
+ memmove(bi->refs + i, bi->refs + i + 1, sizeof(InstructionRef) * (bi->ref_cnt - i - 1));
+ bi->ref_cnt--;
+ }
+ else {
+ i++;
+ }
+ }
+ bi->valid = 1;
+ if (!bi->stepping_over_bp) {
+ if (bi->ref_cnt == 0) {
+ if (bi->planted) remove_instruction(bi);
+ list_remove(&bi->link_all);
+ list_remove(&bi->link_adr);
+ context_unlock(bi->cb.ctx);
+ release_error_report(bi->planting_error);
+ loc_free(bi->refs);
+ loc_free(bi);
+ }
+ else if (!bi->planted) {
+ plant_instruction(bi);
+ }
+ else {
+ unsigned type = 0;
+ ContextAddress size = 0;
+ get_bi_access_types(bi, &type, &size);
+ if (bi->cb.access_types != type || bi->cb.length != size) {
+ remove_instruction(bi);
+ plant_instruction(bi);
+ }
+ }
+ }
+ }
+}
+
+void clone_breakpoints_on_process_fork(Context * parent, Context * child) {
+ Context * mem = context_get_group(parent, CONTEXT_GROUP_PROCESS);
+ LINK * l = instructions.next;
+ while (l != &instructions) {
+ int i;
+ BreakInstruction * ci = NULL;
+ BreakInstruction * bi = link_all2bi(l);
+ l = l->next;
+ if (!bi->planted) continue;
+ if (!bi->saved_size) continue;
+ if (bi->cb.ctx != mem) continue;
+ ci = add_instruction(child, bi->virtual_addr, bi->cb.address);
+ ci->cb.length = bi->cb.length;
+ ci->cb.access_types = bi->cb.access_types;
+ memcpy(ci->saved_code, bi->saved_code, bi->saved_size);
+ ci->saved_size = bi->saved_size;
+ ci->ref_size = bi->ref_size;
+ ci->ref_cnt = bi->ref_cnt;
+ ci->refs = (InstructionRef *)loc_alloc_zero(sizeof(InstructionRef) * ci->ref_size);
+ for (i = 0; i < bi->ref_cnt; i++) {
+ BreakpointInfo * bp = bi->refs[i].bp;
+ ci->refs[i] = bi->refs[i];
+ ci->refs[i].ctx = child;
+ context_lock(child);
+ bp->instruction_cnt++;
+ bp->status_changed = 1;
+ }
+ ci->valid = 1;
+ ci->planted = 1;
+ }
+}
+
+void unplant_breakpoints(Context * ctx) {
+ Context * mem = context_get_group(ctx, CONTEXT_GROUP_PROCESS);
+ LINK * l = instructions.next;
+ while (l != &instructions) {
+ int i;
+ BreakInstruction * bi = link_all2bi(l);
+ l = l->next;
+ if (!bi->planted) continue;
+ if (!bi->saved_size) continue;
+ if (bi->cb.ctx != mem) continue;
+ remove_instruction(bi);
+ for (i = 0; i < bi->ref_cnt; i++) {
+ BreakpointInfo * bp = bi->refs[i].bp;
+ assert(bp->instruction_cnt > 0);
+ bp->instruction_cnt--;
+ bp->status_changed = 1;
+ context_unlock(bi->refs[i].ctx);
+ release_error_report(bi->refs[i].address_error);
+ }
+ list_remove(&bi->link_all);
+ list_remove(&bi->link_adr);
+ context_unlock(bi->cb.ctx);
+ release_error_report(bi->planting_error);
+ loc_free(bi->refs);
+ loc_free(bi);
+ }
+}
+
+int check_breakpoints_on_memory_read(Context * ctx, ContextAddress address, void * p, size_t size) {
+ if (!planting_instruction) {
+ while (size > 0) {
+ size_t sz = size;
+ uint8_t * buf = (uint8_t *)p;
+ LINK * l = instructions.next;
+ Context * mem = NULL;
+ ContextAddress mem_addr = 0;
+ ContextAddress mem_base = 0;
+ ContextAddress mem_size = 0;
+ if (context_get_canonical_addr(ctx, address, &mem, &mem_addr, &mem_base, &mem_size) < 0) return -1;
+ if ((size_t)(mem_base + mem_size - mem_addr) < sz) sz = (size_t)(mem_base + mem_size - mem_addr);
+ while (l != &instructions) {
+ BreakInstruction * bi = link_all2bi(l);
+ size_t i;
+ l = l->next;
+ if (!bi->planted) continue;
+ if (!bi->saved_size) continue;
+ if (bi->cb.ctx != mem) continue;
+ if (bi->cb.address + bi->saved_size <= mem_addr) continue;
+ if (bi->cb.address >= mem_addr + sz) continue;
+ for (i = 0; i < bi->saved_size; i++) {
+ if (bi->cb.address + i < mem_addr) continue;
+ if (bi->cb.address + i >= mem_addr + sz) continue;
+ buf[bi->cb.address + i - mem_addr] = bi->saved_code[i];
+ }
+ }
+ p = (uint8_t *)p + sz;
+ address += sz;
+ size -= sz;
+ }
+ }
+ return 0;
+}
+
+int check_breakpoints_on_memory_write(Context * ctx, ContextAddress address, void * p, size_t size) {
+ if (!planting_instruction) {
+ while (size > 0) {
+ size_t sz = size;
+ uint8_t * buf = (uint8_t *)p;
+ LINK * l = instructions.next;
+ Context * mem = NULL;
+ ContextAddress mem_addr = 0;
+ ContextAddress mem_base = 0;
+ ContextAddress mem_size = 0;
+ if (context_get_canonical_addr(ctx, address, &mem, &mem_addr, &mem_base, &mem_size) < 0) return -1;
+ if ((size_t)(mem_base + mem_size - mem_addr) < sz) sz = (size_t)(mem_base + mem_size - mem_addr);
+ while (l != &instructions) {
+ BreakInstruction * bi = link_all2bi(l);
+ l = l->next;
+ if (!bi->planted) continue;
+ if (!bi->saved_size) continue;
+ if (bi->cb.ctx != mem) continue;
+ if (bi->cb.address + bi->saved_size <= mem_addr) continue;
+ if (bi->cb.address >= mem_addr + sz) continue;
+ {
+ size_t i;
+ uint8_t * break_inst = get_break_instruction(bi->cb.ctx, &i);
+ assert(i == bi->saved_size);
+ for (i = 0; i < bi->saved_size; i++) {
+ if (bi->cb.address + i < mem_addr) continue;
+ if (bi->cb.address + i >= mem_addr + sz) continue;
+ bi->saved_code[i] = buf[bi->cb.address + i - mem_addr];
+ buf[bi->cb.address + i - mem_addr] = break_inst[i];
+ }
+ }
+ }
+ p = (uint8_t *)p + sz;
+ address += sz;
+ size -= sz;
+ }
+ }
+ return 0;
+}
+
+static void write_breakpoint_status(OutputStream * out, BreakpointInfo * bp) {
+ BreakpointAttribute * u = bp->unsupported;
+
+ assert(*bp->id);
+ write_stream(out, '{');
+
+ if (u != NULL) {
+ const char * msg = "Unsupported breakpoint properties: ";
+ json_write_string(out, "Error");
+ write_stream(out, ':');
+ write_stream(out, '"');
+ while (*msg) json_write_char(out, *msg++);
+ while (u != NULL) {
+ msg = u->name;
+ while (*msg) json_write_char(out, *msg++);
+ u = u->next;
+ if (u != NULL) {
+ json_write_char(out, ',');
+ json_write_char(out, ' ');
+ }
+ }
+ write_stream(out, '"');
+ }
+ else if (bp->instruction_cnt) {
+ int cnt = 0;
+ LINK * l = instructions.next;
+ json_write_string(out, "Instances");
+ write_stream(out, ':');
+ write_stream(out, '[');
+ while (l != &instructions) {
+ int i = 0;
+ BreakInstruction * bi = link_all2bi(l);
+ l = l->next;
+ if (bi->virtual_addr && bi->planting_error != NULL) continue;
+ for (i = 0; i < bi->ref_cnt; i++) {
+ if (bi->refs[i].bp != bp) continue;
+ if (cnt > 0) write_stream(out, ',');
+ write_stream(out, '{');
+ json_write_string(out, "LocationContext");
+ write_stream(out, ':');
+ json_write_string(out, bi->refs[i].ctx->id);
+ write_stream(out, ',');
+ if (bi->refs[i].address_error != NULL) {
+ json_write_string(out, "Error");
+ write_stream(out, ':');
+ json_write_string(out, errno_to_str(set_error_report_errno(bi->refs[i].address_error)));
+ }
+ else {
+ json_write_string(out, "Address");
+ write_stream(out, ':');
+ json_write_uint64(out, bi->refs[i].addr);
+ write_stream(out, ',');
+ json_write_string(out, "Size");
+ write_stream(out, ':');
+ json_write_uint64(out, bi->refs[i].size);
+ if (bi->planting_error != NULL) {
+ write_stream(out, ',');
+ json_write_string(out, "Error");
+ write_stream(out, ':');
+ json_write_string(out, errno_to_str(set_error_report_errno(bi->planting_error)));
+ }
+ else if (bi->planted) {
+ write_stream(out, ',');
+ json_write_string(out, "BreakpointType");
+ write_stream(out, ':');
+ json_write_string(out, bi->saved_size ? "Software" : "Hardware");
+ }
+ }
+ write_stream(out, '}');
+ cnt++;
+ }
+ }
+ write_stream(out, ']');
+ assert(cnt > 0);
+ }
+ else if (bp->error) {
+ json_write_string(out, "Error");
+ write_stream(out, ':');
+ json_write_string(out, errno_to_str(set_error_report_errno(bp->error)));
+ }
+
+ write_stream(out, '}');
+}
+
+static void send_event_breakpoint_status(Channel * channel, BreakpointInfo * bp) {
+ OutputStream * out = channel ? &channel->out : &broadcast_group->out;
+ unsigned i;
+
+ write_stringz(out, "E");
+ write_stringz(out, BREAKPOINTS);
+ write_stringz(out, "status");
+
+ json_write_string(out, bp->id);
+ write_stream(out, 0);
+ write_breakpoint_status(out, bp);
+ write_stream(out, 0);
+ write_stream(out, MARKER_EOM);
+ if (channel) return;
+
+ for (i = 0; i < listener_cnt; i++) {
+ Listener * l = listeners + i;
+ if (l->listener->breakpoint_status_changed == NULL) continue;
+ l->listener->breakpoint_status_changed(bp, l->args);
+ }
+}
+
+static InstructionRef * link_breakpoint_instruction(
+ BreakpointInfo * bp, Context * ctx,
+ ContextAddress ctx_addr, ContextAddress size,
+ Context * mem, int virtual_addr, ContextAddress mem_addr,
+ BreakInstruction ** bi_ptr) {
+
+ BreakInstruction * bi = NULL;
+ InstructionRef * ref = NULL;
+
+ bi = find_instruction(mem, virtual_addr, mem_addr);
+ if (bi == NULL) {
+ bi = add_instruction(mem, virtual_addr, mem_addr);
+ }
+ else {
+ int i = 0;
+ while (i < bi->ref_cnt) {
+ ref = bi->refs + i;
+ if (ref->bp == bp && ref->ctx == ctx) {
+ assert(!bi->valid);
+ if (ref->size < size) ref->size = size;
+ ref->addr = ctx_addr;
+ ref->cnt++;
+ if (bi_ptr) *bi_ptr = bi;
+ return ref;
+ }
+ i++;
+ }
+ }
+ if (bi->ref_cnt >= bi->ref_size) {
+ bi->ref_size = bi->ref_size == 0 ? 8 : bi->ref_size * 2;
+ bi->refs = (InstructionRef *)loc_realloc(bi->refs, sizeof(InstructionRef) * bi->ref_size);
+ }
+ ref = bi->refs + bi->ref_cnt++;
+ context_lock(ctx);
+ memset(ref, 0, sizeof(InstructionRef));
+ ref->bp = bp;
+ ref->ctx = ctx;
+ ref->addr = ctx_addr;
+ ref->size = size;
+ ref->cnt = 1;
+ bi->valid = 0;
+ bp->instruction_cnt++;
+ bp->status_changed = 1;
+ if (bi_ptr) *bi_ptr = bi;
+ return ref;
+}
+
+static void address_expression_error(Context * ctx, BreakpointInfo * bp, int error) {
+ ErrorReport * rp = NULL;
+ if (get_error_code(errno) == ERR_CACHE_MISS) return;
+ assert(error != 0);
+ assert(bp->instruction_cnt == 0 || bp->error == NULL);
+ rp = get_error_report(error);
+ assert(rp != NULL);
+ if (ctx != NULL) {
+ InstructionRef * ref = link_breakpoint_instruction(bp, ctx, 0, 0, ctx, 1, 0, NULL);
+ if (!compare_error_reports(rp, ref->address_error)) {
+ release_error_report(ref->address_error);
+ ref->address_error = rp;
+ bp->status_changed = 1;
+ }
+ else {
+ release_error_report(rp);
+ }
+ }
+ else if (!compare_error_reports(rp, bp->error)) {
+ release_error_report(bp->error);
+ bp->error = rp;
+ bp->status_changed = 1;
+ }
+ else {
+ release_error_report(rp);
+ }
+}
+
+static void plant_breakpoint(Context * ctx, BreakpointInfo * bp, ContextAddress addr, ContextAddress size) {
+ Context * mem = NULL;
+ ContextAddress mem_addr = 0;
+
+ if (context_get_supported_bp_access_types(ctx) & CTX_BP_ACCESS_VIRTUAL) {
+ BreakInstruction * bi = NULL;
+ link_breakpoint_instruction(bp, ctx, addr, size, ctx, 1, addr, &bi);
+ if (!bi->planted) plant_instruction(bi);
+ if (bi->planted) return;
+ }
+
+ if (context_get_canonical_addr(ctx, addr, &mem, &mem_addr, NULL, NULL) < 0) {
+ address_expression_error(ctx, bp, errno);
+ }
+ else {
+ link_breakpoint_instruction(bp, ctx, addr, size, mem, 0, mem_addr, NULL);
+ }
+}
+
+static void event_replant_breakpoints(void * arg);
+
+static EvaluationRequest * create_evaluation_request(Context * ctx) {
+ EvaluationRequest * req = EXT(ctx)->req;
+ if (req == NULL) {
+ req = (EvaluationRequest *)loc_alloc_zero(sizeof(EvaluationRequest));
+ req->ctx = ctx;
+ list_init(&req->link_posted);
+ list_init(&req->link_active);
+ EXT(ctx)->req = req;
+ }
+ assert(req->ctx == ctx);
+ assert(req->bp_cnt == 0);
+ return req;
+}
+
+static ConditionEvaluationRequest * add_condition_evaluation_request(EvaluationRequest * req, BreakpointInfo * bp) {
+ int i;
+ ConditionEvaluationRequest * c = NULL;
+
+ assert(bp->instruction_cnt);
+ assert(bp->error == NULL);
+
+ for (i = 0; i < req->bp_cnt; i++) {
+ if (req->bp_arr[i].bp == bp) return NULL;
+ }
+
+ if (req->bp_max <= req->bp_cnt) {
+ req->bp_max = req->bp_cnt + 4;
+ req->bp_arr = (ConditionEvaluationRequest *)loc_realloc(req->bp_arr, sizeof(ConditionEvaluationRequest) * req->bp_max);
+ }
+ c = req->bp_arr + req->bp_cnt++;
+ c->bp = bp;
+ c->condition_ok = 0;
+ c->triggered = 0;
+ return c;
+}
+
+static void post_evaluation_request(EvaluationRequest * req) {
+ if (list_is_empty(&req->link_posted)) {
+ context_lock(req->ctx);
+ list_add_last(&req->link_posted, &evaluations_posted);
+ post_safe_event(req->ctx, event_replant_breakpoints, (void *)++generation_posted);
+ }
+}
+
+static void post_location_evaluation_request(Context * ctx, BreakpointInfo * bp) {
+ ContextExtensionBP * ext = EXT(ctx);
+ Context * grp = context_get_group(ctx, CONTEXT_GROUP_BREAKPOINT);
+ if (ext->bp_grp != NULL && ext->bp_grp != grp && !ext->bp_grp->exited) {
+ /* The context has migrated into another breakpoint group.
+ * If the old group became empty, we need to remove breakpoints in it.
+ */
+ int cnt = 0;
+ LINK * l = context_root.next;
+ while (l != &context_root) {
+ Context * c = ctxl2ctxp(l);
+ l = l->next;
+ if (c->exited) continue;
+ if (context_get_group(c, CONTEXT_GROUP_BREAKPOINT) == ext->bp_grp) cnt++;
+ }
+ if (cnt == 0) {
+ EvaluationRequest * req = create_evaluation_request(ext->bp_grp);
+ req->bp = NULL;
+ req->location = 1;
+ post_evaluation_request(req);
+ EXT(ext->bp_grp)->empty_bp_grp = 1;
+ }
+ }
+ ext->bp_grp = grp;
+ if (grp != NULL) {
+ EvaluationRequest * req = create_evaluation_request(grp);
+ if (!req->location) {
+ req->bp = bp;
+ req->location = 1;
+ post_evaluation_request(req);
+ }
+ else if (req->bp != bp) {
+ req->bp = NULL;
+ }
+ EXT(grp)->empty_bp_grp = 0;
+ }
+}
+
+static void expr_cache_enter(CacheClient * client, BreakpointInfo * bp, Context * ctx) {
+ LINK * l = NULL;
+ EvaluationArgs args;
+
+ args.bp = bp;
+ args.ctx = ctx;
+
+ if (bp->error) {
+ release_error_report(bp->error);
+ bp->error = NULL;
+ bp->status_changed = 1;
+ }
+
+ if (*bp->id) {
+ l = bp->link_clients.next;
+ while (l != &bp->link_clients) {
+ BreakpointRef * br = link_bp2br(l);
+ Channel * c = br->channel;
+ assert(br->bp == bp);
+ cache_enter_cnt++;
+ run_ctrl_lock();
+ if (c == NULL) {
+ client(&args);
+ }
+ else {
+ assert(!is_channel_closed(c));
+ cache_enter(client, c, &args, sizeof(args));
+ }
+ l = l->next;
+ }
+ }
+ else {
+ cache_enter_cnt++;
+ run_ctrl_lock();
+ client(&args);
+ }
+}
+
+static void free_bp(BreakpointInfo * bp) {
+ assert(list_is_empty(&evaluations_posted));
+ assert(list_is_empty(&evaluations_active));
+ assert(list_is_empty(&bp->link_clients));
+ assert(bp->instruction_cnt == 0);
+ assert(bp->client_cnt == 0);
+ list_remove(&bp->link_all);
+ if (*bp->id) list_remove(&bp->link_id);
+ if (bp->ctx) context_unlock(bp->ctx);
+ release_error_report(bp->error);
+ loc_free(bp->location);
+ loc_free(bp->context_ids);
+ loc_free(bp->context_ids_prev);
+ loc_free(bp->context_names);
+ loc_free(bp->context_names_prev);
+ loc_free(bp->stop_group);
+ loc_free(bp->file);
+ loc_free(bp->condition);
+ loc_free(bp->client_data);
+ while (bp->attrs != NULL) {
+ BreakpointAttribute * attr = bp->attrs;
+ bp->attrs = attr->next;
+ loc_free(attr->name);
+ loc_free(attr->value);
+ loc_free(attr);
+ }
+ assert(list_is_empty(&bp->link_clients));
+ loc_free(bp);
+}
+
+static void notify_breakpoints_status(void) {
+ LINK * l = NULL;
+ assert(generation_done == generation_active);
+ for (l = breakpoints.next; l != &breakpoints;) {
+ BreakpointInfo * bp = link_all2bp(l);
+ l = l->next;
+#ifndef NDEBUG
+ {
+ /* Verify breakpoints data structure */
+ LINK * m = NULL;
+ int instruction_cnt = 0;
+ for (m = instructions.next; m != &instructions; m = m->next) {
+ int i;
+ BreakInstruction * bi = link_all2bi(m);
+ assert(bi->valid);
+ assert(bi->ref_cnt <= bi->ref_size);
+ assert(bi->cb.ctx->ref_count > 0);
+ for (i = 0; i < bi->ref_cnt; i++) {
+ assert(bi->refs[i].cnt > 0);
+ if (bi->refs[i].bp == bp) instruction_cnt++;
+ }
+ }
+ assert(bp->enabled || instruction_cnt == 0);
+ assert(bp->instruction_cnt == instruction_cnt);
+ if (*bp->id) {
+ int i;
+ int client_cnt = 0;
+ for (i = 0; i < INP2BR_HASH_SIZE; i++) {
+ for (m = inp2br[i].next; m != &inp2br[i]; m = m->next) {
+ BreakpointRef * br = link_inp2br(m);
+ if (br->bp == bp) client_cnt++;
+ }
+ }
+ assert(bp->client_cnt == client_cnt);
+ }
+ else {
+ assert(list_is_empty(&bp->link_clients));
+ }
+ }
+#endif
+ if (bp->client_cnt == 0) {
+ if (bp->instruction_cnt == 0) free_bp(bp);
+ }
+ else if (bp->status_changed) {
+ if (*bp->id) send_event_breakpoint_status(NULL, bp);
+ bp->status_changed = 0;
+ }
+ }
+}
+
+static void done_condition_evaluation(EvaluationRequest * req) {
+ Context * ctx = req->ctx;
+ size_t size = 0;
+ int i;
+
+ for (i = 0; i < req->bp_cnt; i++) {
+ BreakpointInfo * bp = req->bp_arr[i].bp;
+ if (!req->bp_arr[i].condition_ok) continue;
+ bp->hit_count++;
+ if (bp->hit_count <= bp->ignore_count) continue;
+ bp->hit_count = 0;
+ if (bp->event_callback != NULL) {
+ bp->event_callback(ctx, bp->event_callback_args);
+ }
+ else {
+ assert(bp->id[0] != 0);
+ req->bp_arr[i].triggered = 1;
+ size += sizeof(char *) + strlen(bp->id) + 1;
+ }
+ }
+
+ if (size > 0) {
+ /* Create bp_arr of triggered breakpoint IDs */
+ size_t mem_size = size + sizeof(char *);
+ char ** bp_arr = (char **)loc_alloc(mem_size);
+ char * pool = (char *)bp_arr + mem_size;
+ assert(ctx->stopped);
+ assert(EXT(ctx)->bp_ids == NULL);
+ EXT(ctx)->bp_ids = bp_arr;
+ for (i = 0; i < req->bp_cnt; i++) {
+ BreakpointInfo * bp = req->bp_arr[i].bp;
+ if (req->bp_arr[i].triggered) {
+ size_t n = strlen(bp->id) + 1;
+ pool -= n;
+ memcpy(pool, bp->id, n);
+ *bp_arr++ = pool;
+ }
+ }
+ *bp_arr++ = NULL;
+ assert((char *)bp_arr == pool);
+ for (i = 0; i < req->bp_cnt; i++) {
+ BreakpointInfo * bp = req->bp_arr[i].bp;
+ if (req->bp_arr[i].triggered && bp->stop_group == NULL) {
+ suspend_debug_context(ctx);
+ }
+ }
+ }
+}
+
+static void done_all_evaluations(void) {
+ LINK * l = evaluations_active.next;
+
+ while (l != &evaluations_active) {
+ EvaluationRequest * req = link_active2erl(l);
+ l = l->next;
+ if (req->bp_cnt) {
+ assert(req->ctx->stopped_by_bp || req->ctx->stopped_by_cb);
+ done_condition_evaluation(req);
+ }
+ }
+
+ l = evaluations_active.next;
+ while (l != &evaluations_active) {
+ EvaluationRequest * req = link_active2erl(l);
+ Context * ctx = req->ctx;
+ int i;
+
+ l = l->next;
+
+ /* Intercept contexts in BP stop groups */
+ for (i = 0; i < req->bp_cnt; i++) {
+ BreakpointInfo * bp = req->bp_arr[i].bp;
+ if (req->bp_arr[i].triggered && bp->stop_group != NULL) {
+ char ** ids = bp->stop_group;
+ while (*ids) {
+ Context * c = id2ctx(*ids++);
+ if (c != NULL) suspend_debug_context(c);
+ }
+ }
+ }
+
+ req->bp_cnt = 0;
+ list_remove(&req->link_active);
+ context_unlock(ctx);
+ }
+
+ if (list_is_empty(&evaluations_posted)) {
+ assert(cache_enter_cnt == 0);
+ assert(generation_done != generation_active);
+ flush_instructions();
+ generation_done = generation_active;
+ notify_breakpoints_status();
+ }
+}
+
+static void done_evaluation(void) {
+ assert(cache_enter_cnt > 0);
+ cache_enter_cnt--;
+ if (cache_enter_cnt == 0) {
+ done_all_evaluations();
+ if (!list_is_empty(&evaluations_posted)) {
+ EvaluationRequest * req = link_posted2erl(evaluations_posted.next);
+ post_safe_event(req->ctx, event_replant_breakpoints, (void *)++generation_posted);
+ }
+ }
+}
+
+static void expr_cache_exit(EvaluationArgs * args) {
+ if (*args->bp->id) cache_exit();
+ done_evaluation();
+ run_ctrl_unlock();
+}
+
+static void plant_at_address_expression(Context * ctx, ContextAddress ip, BreakpointInfo * bp) {
+ ContextAddress addr = 0;
+ ContextAddress size = 1;
+ int error = 0;
+ Value v;
+
+ if (evaluate_expression(ctx, STACK_NO_FRAME, ip, bp->location, 1, &v) < 0) error = errno;
+ if (!error && value_to_address(&v, &addr) < 0) error = errno;
+ if (bp->access_mode & (CTX_BP_ACCESS_DATA_READ | CTX_BP_ACCESS_DATA_WRITE)) {
+ if (bp->access_size > 0) {
+ size = bp->access_size;
+ }
+ else {
+ size = context_word_size(ctx);
+#if ENABLE_Symbols
+ {
+ Symbol * type = v.type;
+ if (type != NULL) {
+ int type_class = 0;
+ Symbol * base_type = NULL;
+ if (!error && get_symbol_type_class(type, &type_class) < 0) error = errno;
+ if (!error && type_class != TYPE_CLASS_POINTER) error = set_errno(ERR_INV_DATA_TYPE, "Pointer expected");
+ if (!error && get_symbol_base_type(type, &base_type) < 0) error = errno;
+ if (!error && base_type != NULL && get_symbol_size(base_type, &size) < 0) error = errno;
+ }
+ }
+#endif
+ }
+ }
+ if (error) address_expression_error(ctx, bp, error);
+ else plant_breakpoint(ctx, bp, addr, size);
+}
+
+static void evaluate_address_expression(void * x) {
+ EvaluationArgs * args = (EvaluationArgs *)x;
+ assert(cache_enter_cnt > 0);
+ plant_at_address_expression(args->ctx, 0, args->bp);
+ expr_cache_exit(args);
+}
+
+#if ENABLE_LineNumbers
+static ContextAddress bp_ip = 0;
+
+static void plant_breakpoint_address_iterator(CodeArea * area, void * x) {
+ EvaluationArgs * args = (EvaluationArgs *)x;
+ if (args->bp->location == NULL) {
+ plant_breakpoint(args->ctx, args->bp, area->start_address, 1);
+ }
+ else {
+ /* TODO: cannot call plant_at_address_expression() here because elf_list_first() is not re-entrant */
+ if (bp_ip == 0) bp_ip = area->start_address;
+ }
+}
+
+static void evaluate_text_location(void * x) {
+ EvaluationArgs * args = (EvaluationArgs *)x;
+ BreakpointInfo * bp = args->bp;
+
+ bp_ip = 0;
+ assert(cache_enter_cnt > 0);
+ if (line_to_address(args->ctx, bp->file, bp->line, bp->column, plant_breakpoint_address_iterator, args) < 0) {
+ address_expression_error(args->ctx, bp, errno);
+ }
+ if (bp_ip != 0) plant_at_address_expression(args->ctx, bp_ip, args->bp);
+ expr_cache_exit(args);
+}
+#endif
+
+static int check_context_ids_location(BreakpointInfo * bp, Context * ctx) {
+ /* Check context IDs attribute and return 1 if the breakpoint should be planted in 'ctx' */
+ assert(ctx == context_get_group(ctx, CONTEXT_GROUP_BREAKPOINT));
+ if (bp->ctx != NULL) {
+ if (context_get_group(bp->ctx, CONTEXT_GROUP_BREAKPOINT) != ctx) return 0;
+ }
+ if (bp->context_ids != NULL) {
+ int ok = 0;
+ char ** ids = bp->context_ids;
+ while (!ok && *ids != NULL) {
+ Context * c = id2ctx(*ids++);
+ if (c == NULL) continue;
+ ok = context_get_group(c, CONTEXT_GROUP_BREAKPOINT) == ctx;
+ }
+ if (!ok) return 0;
+ }
+ if (bp->context_names != NULL) {
+ int ok = 0;
+ char ** names = bp->context_names;
+ while (!ok && *names != NULL) {
+ char * name = *names++;
+ LINK * l = context_root.next;
+ while (!ok && l != &context_root) {
+ Context * c = ctxl2ctxp(l);
+ l = l->next;
+ if (c->exited) continue;
+ if (c->name == NULL) continue;
+ if (strcmp(ctx->name, name)) continue;
+ ok = context_get_group(c, CONTEXT_GROUP_BREAKPOINT) == ctx;
+ }
+ }
+ if (!ok) return 0;
+ }
+ return 1;
+}
+
+static int check_context_ids_condition(BreakpointInfo * bp, Context * ctx) {
+ /* Check context IDs attribute and return 1 if the breakpoint should be triggered by 'ctx' */
+ assert(context_has_state(ctx));
+ if (bp->ctx != NULL) {
+ if (bp->ctx != ctx) return 0;
+ }
+ if (bp->context_ids != NULL) {
+ int ok = 0;
+ char ** ids = bp->context_ids;
+ Context * prs = context_get_group(ctx, CONTEXT_GROUP_PROCESS);
+ while (!ok && *ids != NULL) {
+ char * id = *ids++;
+ ok = strcmp(id, ctx->id) == 0 || (prs && strcmp(id, prs->id) == 0);
+ }
+ if (!ok) return 0;
+ }
+ if (bp->context_names != NULL) {
+ int ok = 0;
+ if (ctx->name) {
+ char * name = ctx->name;
+ char ** names = bp->context_names;
+ while (!ok && *names != NULL) {
+ ok = strcmp(name, *names++) == 0;
+ }
+ }
+ if (!ok) {
+ Context * prs = context_get_group(ctx, CONTEXT_GROUP_PROCESS);
+ if (prs && prs->name) {
+ char * name = prs->name;
+ char ** names = bp->context_names;
+ while (!ok && *names != NULL) {
+ ok = strcmp(name, *names++) == 0;
+ }
+ }
+ }
+ if (!ok) return 0;
+ }
+ return 1;
+}
+
+static void evaluate_condition(void * x) {
+ int i;
+ EvaluationArgs * args = (EvaluationArgs *)x;
+ Context * ctx = args->ctx;
+ BreakpointInfo * bp = args->bp;
+ EvaluationRequest * req = EXT(ctx)->req;
+
+ assert(req != NULL);
+ assert(req->bp_cnt > 0);
+ assert(ctx->stopped);
+ assert(ctx->stopped_by_bp || ctx->stopped_by_cb);
+ assert(cache_enter_cnt > 0);
+
+ for (i = 0; i < req->bp_cnt; i++) {
+ if (bp != req->bp_arr[i].bp) continue;
+
+ if (is_disabled(bp)) break;
+ if (!check_context_ids_condition(bp, ctx)) break;
+
+ if (bp->condition != NULL) {
+ Value v;
+ int b = 0;
+ if (evaluate_expression(ctx, STACK_TOP_FRAME, 0, bp->condition, 1, &v) < 0 || value_to_boolean(&v, &b) < 0) {
+ int no = get_error_code(errno);
+ if (no == ERR_CACHE_MISS) break;
+ if (no == ERR_CHANNEL_CLOSED) break;
+ trace(LOG_ALWAYS, "%s: %s", errno_to_str(errno), bp->condition);
+ req->bp_arr[i].condition_ok = 1;
+ }
+ else if (b) {
+ req->bp_arr[i].condition_ok = 1;
+ }
+ break;
+ }
+
+ req->bp_arr[i].condition_ok = 1;
+ break;
+ }
+
+ expr_cache_exit(args);
+}
+
+static void evaluate_bp_location(BreakpointInfo * bp, Context * ctx) {
+ if (is_disabled(bp)) return;
+ if (!check_context_ids_location(bp, ctx)) return;
+ if (bp->file != NULL) {
+#if ENABLE_LineNumbers
+ expr_cache_enter(evaluate_text_location, bp, ctx);
+#else
+ set_errno(ERR_UNSUPPORTED, "LineNumbers service not available");
+ address_expression_error(NULL, bp, errno);
+#endif
+ }
+ else if (bp->location != NULL) {
+ expr_cache_enter(evaluate_address_expression, bp, ctx);
+ }
+ else {
+ address_expression_error(NULL, bp, ERR_INV_EXPRESSION);
+ }
+}
+
+static void event_replant_breakpoints(void * arg) {
+ LINK * q;
+
+ assert(!list_is_empty(&evaluations_posted));
+ if ((uintptr_t)arg != generation_posted) return;
+ if (cache_enter_cnt > 0) return;
+
+ assert(list_is_empty(&evaluations_active));
+ cache_enter_cnt++;
+ generation_active = generation_posted;
+ q = evaluations_posted.next;
+ while (q != &evaluations_posted) {
+ EvaluationRequest * req = link_posted2erl(q);
+ Context * ctx = req->ctx;
+ q = q->next;
+ list_remove(&req->link_posted);
+ list_add_first(&req->link_active, &evaluations_active);
+ if (req->location) {
+ BreakpointInfo * bp = req->bp;
+ req->location = 0;
+ req->bp = NULL;
+ clear_instruction_refs(ctx, bp);
+ if (!ctx->exiting && !ctx->exited && !EXT(ctx)->empty_bp_grp) {
+ context_lock(ctx);
+ if (bp != NULL) {
+ evaluate_bp_location(bp, ctx);
+ }
+ else {
+ LINK * l = breakpoints.next;
+ while (l != &breakpoints) {
+ evaluate_bp_location(link_all2bp(l), ctx);
+ l = l->next;
+ }
+ }
+ context_unlock(ctx);
+ }
+ }
+ if (req->bp_cnt > 0) {
+ int i;
+ for (i = 0; i < req->bp_cnt; i++) {
+ req->bp_arr[i].condition_ok = 0;
+ expr_cache_enter(evaluate_condition, req->bp_arr[i].bp, ctx);
+ }
+ }
+ }
+ done_evaluation();
+}
+
+static char ** str_arr_dup(char ** x) {
+ int n = 0;
+ int l = 0;
+ int i = 0;
+ int offs = 0;
+ char ** y = NULL;
+ if (x == NULL) return NULL;
+ while (x[n] != NULL) l += strlen(x[n++]) + 1;
+ offs = sizeof(char *) * (n + 1);
+ l += offs;
+ y = (char **)loc_alloc_zero(l);
+ while (i < n) {
+ y[i] = strcpy((char *)y + offs, x[i]);
+ offs += strlen(x[i++]) + 1;
+ }
+ assert(offs == l);
+ return y;
+}
+
+static int str_arr_equ(char ** x, char ** y) {
+ if (x == y) return 1;
+ if (x == NULL || y == NULL) return 0;
+ while (*x != NULL && *y != NULL) {
+ if (strcmp(*x++, *y++) != 0) return 0;
+ }
+ return *x == *y;
+}
+
+static void replant_breakpoint(BreakpointInfo * bp) {
+ if (bp->instruction_cnt == 0) {
+ if (bp->client_cnt == 0) return;
+ if (list_is_empty(&context_root)) return;
+ if (bp->ctx != NULL && bp->ctx->exited) return;
+ }
+ if (bp->ctx != NULL) {
+ post_location_evaluation_request(bp->ctx, bp);
+ }
+ else if (bp->context_ids && bp->context_ids_prev) {
+ char ** ids = bp->context_ids;
+ while (*ids != NULL) {
+ Context * ctx = id2ctx(*ids++);
+ if (ctx == NULL) continue;
+ if (ctx->exited) continue;
+ post_location_evaluation_request(ctx, bp);
+ }
+ if (!str_arr_equ(bp->context_ids, bp->context_ids_prev)) {
+ ids = bp->context_ids_prev;
+ while (*ids != NULL) {
+ Context * ctx = id2ctx(*ids++);
+ if (ctx == NULL) continue;
+ if (ctx->exited) continue;
+ post_location_evaluation_request(ctx, bp);
+ }
+ bp->context_ids_prev = str_arr_dup(bp->context_ids);
+ }
+ }
+ else if (bp->context_names && bp->context_names_prev) {
+ char ** names = bp->context_names;
+ while (*names != NULL) {
+ char * name = *names++;
+ LINK * l = context_root.next;
+ while (l != &context_root) {
+ Context * ctx = ctxl2ctxp(l);
+ l = l->next;
+ if (ctx->exited) continue;
+ if (ctx->name == NULL) continue;
+ if (strcmp(ctx->name, name)) continue;
+ post_location_evaluation_request(ctx, bp);
+ }
+ }
+ if (!str_arr_equ(bp->context_names, bp->context_names_prev)) {
+ names = bp->context_names_prev;
+ while (*names != NULL) {
+ char * name = *names++;
+ LINK * l = context_root.next;
+ while (l != &context_root) {
+ Context * ctx = ctxl2ctxp(l);
+ l = l->next;
+ if (ctx->exited) continue;
+ if (ctx->name == NULL) continue;
+ if (strcmp(ctx->name, name)) continue;
+ post_location_evaluation_request(ctx, bp);
+ }
+ }
+ bp->context_names_prev = str_arr_dup(bp->context_names);
+ }
+ }
+ else {
+ LINK * l = context_root.next;
+ while (l != &context_root) {
+ Context * ctx = ctxl2ctxp(l);
+ l = l->next;
+ if (ctx->exited) continue;
+ post_location_evaluation_request(ctx, bp);
+ }
+ bp->context_ids_prev = str_arr_dup(bp->context_ids);
+ }
+}
+
+static BreakpointInfo * find_breakpoint(char * id) {
+ int hash = id2bp_hash(id);
+ LINK * l = id2bp[hash].next;
+ while (l != id2bp + hash) {
+ BreakpointInfo * bp = link_id2bp(l);
+ l = l->next;
+ if (strcmp(bp->id, id) == 0) return bp;
+ }
+ return NULL;
+}
+
+static BreakpointRef * find_breakpoint_ref(BreakpointInfo * bp, Channel * channel) {
+ LINK * l;
+ if (bp == NULL) return NULL;
+ l = bp->link_clients.next;
+ while (l != &bp->link_clients) {
+ BreakpointRef * br = link_bp2br(l);
+ assert(br->bp == bp);
+ if (br->channel == channel) return br;
+ l = l->next;
+ }
+ return NULL;
+}
+
+static BreakpointAttribute * read_breakpoint_properties(InputStream * inp) {
+ BreakpointAttribute * attrs = NULL;
+ if (read_stream(inp) != '{') exception(ERR_JSON_SYNTAX);
+ if (peek_stream(inp) == '}') {
+ read_stream(inp);
+ }
+ else {
+ BreakpointAttribute ** p = &attrs;
+ for (;;) {
+ int ch;
+ char name[256];
+ BreakpointAttribute * attr = (BreakpointAttribute *)loc_alloc(sizeof(BreakpointAttribute));
+
+ json_read_string(inp, name, sizeof(name));
+ if (read_stream(inp) != ':') exception(ERR_JSON_SYNTAX);
+ attr->name = loc_strdup(name);
+ attr->value = json_read_object(inp);
+ *p = attr;
+ p = &attr->next;
+ attr->next = NULL;
+
+ ch = read_stream(inp);
+ if (ch == ',') continue;
+ if (ch == '}') break;
+ exception(ERR_JSON_SYNTAX);
+ }
+ }
+ return attrs;
+}
+
+static void read_id_attribute(BreakpointAttribute * attrs, char * id, size_t id_size) {
+ while (attrs != NULL) {
+ if (strcmp(attrs->name, BREAKPOINT_ID) == 0) {
+ ByteArrayInputStream buf;
+ InputStream * inp = create_byte_array_input_stream(&buf, attrs->value, strlen(attrs->value));
+ json_read_string(inp, id, id_size);
+ if (read_stream(inp) != MARKER_EOS) exception(ERR_JSON_SYNTAX);
+ return;
+ }
+ attrs = attrs->next;
+ }
+ str_exception(ERR_OTHER, "Breakpoint must have an ID");
+}
+
+static void set_breakpoint_attribute(BreakpointInfo * bp, const char * name, const char * value) {
+ BreakpointAttribute * attr = bp->attrs;
+ BreakpointAttribute ** ref = &bp->attrs;
+
+ while (attr != NULL) {
+ if (strcmp(attr->name, name) == 0) {
+ loc_free(attr->value);
+ attr->value = loc_strdup(value);
+ return;
+ }
+ ref = &attr->next;
+ attr = attr->next;
+ }
+ attr = (BreakpointAttribute *)loc_alloc_zero(sizeof(BreakpointAttribute));
+ attr->name = loc_strdup(name);
+ attr->value = loc_strdup(value);
+ *ref = attr;
+}
+
+static int set_breakpoint_attributes(BreakpointInfo * bp, BreakpointAttribute * new_attrs) {
+ int diff = 0;
+ BreakpointAttribute * old_attrs = bp->attrs;
+ BreakpointAttribute ** new_ref = &bp->attrs;
+ bp->attrs = NULL;
+
+ while (new_attrs != NULL) {
+ BreakpointAttribute * new_attr = new_attrs;
+ BreakpointAttribute * old_attr = old_attrs;
+ BreakpointAttribute ** old_ref = &old_attrs;
+ InputStream * buf_inp = NULL;
+ ByteArrayInputStream buf;
+ int unsupported_attr = 0;
+ char * name = new_attr->name;
+
+ new_attrs = new_attr->next;
+ new_attr->next = NULL;
+ while (old_attr && strcmp(old_attr->name, name)) {
+ old_ref = &old_attr->next;
+ old_attr = old_attr->next;
+ }
+
+ if (old_attr != NULL) {
+ assert(old_attr == *old_ref);
+ *old_ref = old_attr->next;
+ old_attr->next = NULL;
+ if (strcmp(old_attr->value, new_attr->value) == 0) {
+ *new_ref = old_attr;
+ new_ref = &old_attr->next;
+ loc_free(new_attr->value);
+ loc_free(new_attr->name);
+ loc_free(new_attr);
+ continue;
+ }
+ diff++;
+ loc_free(old_attr->value);
+ loc_free(old_attr->name);
+ loc_free(old_attr);
+ old_attr = NULL;
+ }
+
+ *new_ref = new_attr;
+ new_ref = &new_attr->next;
+
+ buf_inp = create_byte_array_input_stream(&buf, new_attr->value, strlen(new_attr->value));
+
+ if (strcmp(name, BREAKPOINT_ID) == 0) {
+ json_read_string(buf_inp, bp->id, sizeof(bp->id));
+ }
+ else if (strcmp(name, BREAKPOINT_LOCATION) == 0) {
+ loc_free(bp->location);
+ bp->location = json_read_alloc_string(buf_inp);
+ }
+ else if (strcmp(name, BREAKPOINT_ACCESSMODE) == 0) {
+ bp->access_mode = json_read_long(buf_inp);
+ }
+ else if (strcmp(name, BREAKPOINT_SIZE) == 0) {
+ bp->access_size = json_read_long(buf_inp);
+ }
+ else if (strcmp(name, BREAKPOINT_CONDITION) == 0) {
+ loc_free(bp->condition);
+ bp->condition = json_read_alloc_string(buf_inp);
+ }
+ else if (strcmp(name, BREAKPOINT_CONTEXTIDS) == 0) {
+ loc_free(bp->context_ids);
+ bp->context_ids = json_read_alloc_string_array(buf_inp, NULL);
+ }
+ else if (strcmp(name, BREAKPOINT_CONTEXTNAMES) == 0) {
+ loc_free(bp->context_names);
+ bp->context_names = json_read_alloc_string_array(buf_inp, NULL);
+ }
+ else if (strcmp(name, BREAKPOINT_STOP_GROUP) == 0) {
+ loc_free(bp->stop_group);
+ bp->stop_group = json_read_alloc_string_array(buf_inp, NULL);
+ }
+ else if (strcmp(name, BREAKPOINT_FILE) == 0) {
+ loc_free(bp->file);
+ bp->file = json_read_alloc_string(buf_inp);
+ }
+ else if (strcmp(name, BREAKPOINT_LINE) == 0) {
+ bp->line = json_read_long(buf_inp);
+ }
+ else if (strcmp(name, BREAKPOINT_COLUMN) == 0) {
+ bp->column = json_read_long(buf_inp);
+ }
+ else if (strcmp(name, BREAKPOINT_IGNORECOUNT) == 0) {
+ bp->ignore_count = json_read_long(buf_inp);
+ }
+ else if (strcmp(name, BREAKPOINT_ENABLED) == 0) {
+ bp->enabled = json_read_boolean(buf_inp);
+ }
+ else {
+ unsupported_attr = 1;
+ }
+
+ if (!unsupported_attr && read_stream(buf_inp) != MARKER_EOS) exception(ERR_JSON_SYNTAX);
+ }
+
+ while (old_attrs != NULL) {
+ BreakpointAttribute * old_attr = old_attrs;
+ char * name = old_attr->name;
+ old_attrs = old_attr->next;
+
+ if (strcmp(name, BREAKPOINT_ID) == 0) {
+ bp->id[0] = 0;
+ }
+ else if (strcmp(name, BREAKPOINT_LOCATION) == 0) {
+ loc_free(bp->location);
+ bp->location = NULL;
+ }
+ else if (strcmp(name, BREAKPOINT_ACCESSMODE) == 0) {
+ bp->access_mode = 0;
+ }
+ else if (strcmp(name, BREAKPOINT_SIZE) == 0) {
+ bp->access_size = 0;
+ }
+ else if (strcmp(name, BREAKPOINT_CONDITION) == 0) {
+ loc_free(bp->condition);
+ bp->condition = NULL;
+ }
+ else if (strcmp(name, BREAKPOINT_CONTEXTIDS) == 0) {
+ loc_free(bp->context_ids);
+ bp->context_ids = NULL;
+ }
+ else if (strcmp(name, BREAKPOINT_CONTEXTNAMES) == 0) {
+ loc_free(bp->context_names);
+ bp->context_names = NULL;
+ }
+ else if (strcmp(name, BREAKPOINT_STOP_GROUP) == 0) {
+ loc_free(bp->stop_group);
+ bp->stop_group = NULL;
+ }
+ else if (strcmp(name, BREAKPOINT_FILE) == 0) {
+ loc_free(bp->file);
+ bp->file = NULL;
+ }
+ else if (strcmp(name, BREAKPOINT_LINE) == 0) {
+ bp->line = 0;
+ }
+ else if (strcmp(name, BREAKPOINT_COLUMN) == 0) {
+ bp->column = 0;
+ }
+ else if (strcmp(name, BREAKPOINT_IGNORECOUNT) == 0) {
+ bp->ignore_count = 0;
+ }
+ else if (strcmp(name, BREAKPOINT_ENABLED) == 0) {
+ bp->enabled = 0;
+ }
+
+ loc_free(old_attr->value);
+ loc_free(old_attr->name);
+ loc_free(old_attr);
+ diff++;
+ }
+
+ return diff;
+}
+
+static void write_breakpoint_properties(OutputStream * out, BreakpointInfo * bp) {
+ int cnt = 0;
+ BreakpointAttribute * attr = bp->attrs;
+
+ write_stream(out, '{');
+
+ while (attr != NULL) {
+ if (cnt > 0) write_stream(out, ',');
+ json_write_string(out, attr->name);
+ write_stream(out, ':');
+ write_string(out, attr->value);
+ attr = attr->next;
+ cnt++;
+ }
+
+ write_stream(out, '}');
+}
+
+static void send_event_context_added(Channel * channel, BreakpointInfo * bp) {
+ OutputStream * out = channel ? &channel->out : &broadcast_group->out;
+ unsigned i;
+
+ write_stringz(out, "E");
+ write_stringz(out, BREAKPOINTS);
+ write_stringz(out, "contextAdded");
+
+ write_stream(out, '[');
+ write_breakpoint_properties(out, bp);
+ write_stream(out, ']');
+ write_stream(out, 0);
+ write_stream(out, MARKER_EOM);
+ if (channel) return;
+
+ for (i = 0; i < listener_cnt; i++) {
+ Listener * l = listeners + i;
+ if (l->listener->breakpoint_created == NULL) continue;
+ l->listener->breakpoint_created(bp, l->args);
+ }
+}
+
+static void send_event_context_changed(BreakpointInfo * bp) {
+ OutputStream * out = &broadcast_group->out;
+ unsigned i;
+
+ write_stringz(out, "E");
+ write_stringz(out, BREAKPOINTS);
+ write_stringz(out, "contextChanged");
+
+ write_stream(out, '[');
+ write_breakpoint_properties(out, bp);
+ write_stream(out, ']');
+ write_stream(out, 0);
+ write_stream(out, MARKER_EOM);
+
+ for (i = 0; i < listener_cnt; i++) {
+ Listener * l = listeners + i;
+ if (l->listener->breakpoint_changed == NULL) continue;
+ l->listener->breakpoint_changed(bp, l->args);
+ }
+}
+
+static void send_event_context_removed(BreakpointInfo * bp) {
+ OutputStream * out = &broadcast_group->out;
+ unsigned i;
+
+ write_stringz(out, "E");
+ write_stringz(out, BREAKPOINTS);
+ write_stringz(out, "contextRemoved");
+
+ write_stream(out, '[');
+ json_write_string(out, bp->id);
+ write_stream(out, ']');
+ write_stream(out, 0);
+ write_stream(out, MARKER_EOM);
+
+ for (i = 0; i < listener_cnt; i++) {
+ Listener * l = listeners + i;
+ if (l->listener->breakpoint_deleted == NULL) continue;
+ l->listener->breakpoint_deleted(bp, l->args);
+ }
+}
+
+static BreakpointInfo * add_breakpoint(Channel * c, BreakpointAttribute * attrs) {
+ char id[256];
+ BreakpointRef * r = NULL;
+ BreakpointInfo * bp = NULL;
+ int added = 0;
+ int chng = 0;
+
+ read_id_attribute(attrs, id, sizeof(id));
+ bp = find_breakpoint(id);
+ if (bp == NULL) {
+ int hash = id2bp_hash(id);
+ bp = (BreakpointInfo *)loc_alloc_zero(sizeof(BreakpointInfo));
+ list_init(&bp->link_clients);
+ list_add_last(&bp->link_all, &breakpoints);
+ list_add_last(&bp->link_id, id2bp + hash);
+ }
+ chng = set_breakpoint_attributes(bp, attrs);
+ if (list_is_empty(&bp->link_clients)) added = 1;
+ else r = find_breakpoint_ref(bp, c);
+ if (r == NULL) {
+ unsigned inp_hash = (unsigned)(uintptr_t)c / 16 % INP2BR_HASH_SIZE;
+ r = (BreakpointRef *)loc_alloc_zero(sizeof(BreakpointRef));
+ list_add_last(&r->link_inp, inp2br + inp_hash);
+ list_add_last(&r->link_bp, &bp->link_clients);
+ r->channel = c;
+ r->bp = bp;
+ bp->client_cnt++;
+ }
+ assert(r->bp == bp);
+ assert(!list_is_empty(&bp->link_clients));
+ if (chng || added) replant_breakpoint(bp);
+ if (added) send_event_context_added(NULL, bp);
+ else if (chng) send_event_context_changed(bp);
+ return bp;
+}
+
+static void remove_ref(Channel * c, BreakpointRef * br) {
+ BreakpointInfo * bp = br->bp;
+ bp->client_cnt--;
+ list_remove(&br->link_inp);
+ list_remove(&br->link_bp);
+ loc_free(br);
+ if (list_is_empty(&bp->link_clients)) {
+ send_event_context_removed(bp);
+ assert(bp->client_cnt == 0);
+ replant_breakpoint(bp);
+ }
+}
+
+static void delete_breakpoint_refs(Channel * c) {
+ unsigned hash = (unsigned)(uintptr_t)c / 16 % INP2BR_HASH_SIZE;
+ LINK * l = inp2br[hash].next;
+ while (l != &inp2br[hash]) {
+ BreakpointRef * br = link_inp2br(l);
+ l = l->next;
+ if (br->channel == c) remove_ref(c, br);
+ }
+}
+
+static void command_ini_bps(char * token, Channel * c) {
+ int ch;
+ LINK * l = NULL;
+
+ /* Delete all breakpoints of this channel */
+ delete_breakpoint_refs(c);
+
+ /* Report breakpoints from other channels */
+ l = breakpoints.next;
+ while (l != &breakpoints) {
+ BreakpointInfo * bp = link_all2bp(l);
+ l = l->next;
+ if (list_is_empty(&bp->link_clients)) continue;
+ assert(*bp->id);
+ send_event_context_added(c, bp);
+ send_event_breakpoint_status(c, bp);
+ }
+
+ /* Add breakpoints for this channel */
+ ch = read_stream(&c->inp);
+ if (ch == 'n') {
+ if (read_stream(&c->inp) != 'u') exception(ERR_JSON_SYNTAX);
+ if (read_stream(&c->inp) != 'l') exception(ERR_JSON_SYNTAX);
+ if (read_stream(&c->inp) != 'l') exception(ERR_JSON_SYNTAX);
+ }
+ else {
+ if (ch != '[') exception(ERR_PROTOCOL);
+ if (peek_stream(&c->inp) == ']') {
+ read_stream(&c->inp);
+ }
+ else {
+ for (;;) {
+ int ch;
+ add_breakpoint(c, read_breakpoint_properties(&c->inp));
+ ch = read_stream(&c->inp);
+ if (ch == ',') continue;
+ if (ch == ']') break;
+ exception(ERR_JSON_SYNTAX);
+ }
+ }
+ }
+ if (read_stream(&c->inp) != 0) exception(ERR_JSON_SYNTAX);
+ if (read_stream(&c->inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
+
+ write_stringz(&c->out, "R");
+ write_stringz(&c->out, token);
+ write_errno(&c->out, 0);
+ write_stream(&c->out, MARKER_EOM);
+}
+
+static void command_get_bp_ids(char * token, Channel * c) {
+ LINK * l = breakpoints.next;
+ int cnt = 0;
+
+ if (read_stream(&c->inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
+ write_stringz(&c->out, "R");
+ write_stringz(&c->out, token);
+ write_errno(&c->out, 0);
+ write_stream(&c->out, '[');
+
+ while (l != &breakpoints) {
+ BreakpointInfo * bp = link_all2bp(l);
+ l = l->next;
+ if (list_is_empty(&bp->link_clients)) continue;
+ assert(*bp->id);
+ if (cnt > 0) write_stream(&c->out, ',');
+ json_write_string(&c->out, bp->id);
+ cnt++;
+ }
+
+ write_stream(&c->out, ']');
+ write_stream(&c->out, 0);
+ write_stream(&c->out, MARKER_EOM);
+}
+
+static void command_get_properties(char * token, Channel * c) {
+ char id[256];
+ BreakpointInfo * bp = NULL;
+ int err = 0;
+
+ 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);
+
+ bp = find_breakpoint(id);
+ if (bp == NULL || list_is_empty(&bp->link_clients)) err = ERR_INV_CONTEXT;
+
+ write_stringz(&c->out, "R");
+ write_stringz(&c->out, token);
+ write_errno(&c->out, err);
+ if (err) {
+ write_stringz(&c->out, "null");
+ }
+ else {
+ write_breakpoint_properties(&c->out, bp);
+ write_stream(&c->out, 0);
+ }
+ write_stream(&c->out, MARKER_EOM);
+}
+
+static void command_get_status(char * token, Channel * c) {
+ char id[256];
+ BreakpointInfo * bp = NULL;
+ int err = 0;
+
+ 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);
+
+ bp = find_breakpoint(id);
+ if (bp == NULL || list_is_empty(&bp->link_clients)) err = ERR_INV_CONTEXT;
+
+ write_stringz(&c->out, "R");
+ write_stringz(&c->out, token);
+ write_errno(&c->out, err);
+ if (err) {
+ write_stringz(&c->out, "null");
+ }
+ else {
+ write_breakpoint_status(&c->out, bp);
+ write_stream(&c->out, 0);
+ }
+ write_stream(&c->out, MARKER_EOM);
+}
+
+static void command_bp_add(char * token, Channel * c) {
+ BreakpointAttribute * props = read_breakpoint_properties(&c->inp);
+ if (read_stream(&c->inp) != 0) exception(ERR_JSON_SYNTAX);
+ if (read_stream(&c->inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
+
+ add_breakpoint(c, props);
+
+ write_stringz(&c->out, "R");
+ write_stringz(&c->out, token);
+ write_errno(&c->out, 0);
+ write_stream(&c->out, MARKER_EOM);
+}
+
+static void command_bp_change(char * token, Channel * c) {
+ BreakpointAttribute * props = read_breakpoint_properties(&c->inp);
+ if (read_stream(&c->inp) != 0) exception(ERR_JSON_SYNTAX);
+ if (read_stream(&c->inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
+
+ add_breakpoint(c, props);
+
+ write_stringz(&c->out, "R");
+ write_stringz(&c->out, token);
+ write_errno(&c->out, 0);
+ write_stream(&c->out, MARKER_EOM);
+}
+
+static void command_bp_enable(char * token, Channel * c) {
+ int ch = read_stream(&c->inp);
+ if (ch == 'n') {
+ if (read_stream(&c->inp) != 'u') exception(ERR_JSON_SYNTAX);
+ if (read_stream(&c->inp) != 'l') exception(ERR_JSON_SYNTAX);
+ if (read_stream(&c->inp) != 'l') exception(ERR_JSON_SYNTAX);
+ }
+ else {
+ if (ch != '[') exception(ERR_PROTOCOL);
+ if (peek_stream(&c->inp) == ']') {
+ read_stream(&c->inp);
+ }
+ else {
+ for (;;) {
+ int ch;
+ char id[256];
+ BreakpointInfo * bp;
+ json_read_string(&c->inp, id, sizeof(id));
+ bp = find_breakpoint(id);
+ if (bp != NULL && !list_is_empty(&bp->link_clients) && !bp->enabled) {
+ bp->enabled = 1;
+ bp->hit_count = 0;
+ set_breakpoint_attribute(bp, BREAKPOINT_ENABLED, "true");
+ replant_breakpoint(bp);
+ send_event_context_changed(bp);
+ }
+ ch = read_stream(&c->inp);
+ if (ch == ',') continue;
+ if (ch == ']') break;
+ exception(ERR_JSON_SYNTAX);
+ }
+ }
+ }
+ if (read_stream(&c->inp) != 0) exception(ERR_JSON_SYNTAX);
+ if (read_stream(&c->inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
+
+ write_stringz(&c->out, "R");
+ write_stringz(&c->out, token);
+ write_errno(&c->out, 0);
+ write_stream(&c->out, MARKER_EOM);
+}
+
+static void command_bp_disable(char * token, Channel * c) {
+ int ch = read_stream(&c->inp);
+ if (ch == 'n') {
+ if (read_stream(&c->inp) != 'u') exception(ERR_JSON_SYNTAX);
+ if (read_stream(&c->inp) != 'l') exception(ERR_JSON_SYNTAX);
+ if (read_stream(&c->inp) != 'l') exception(ERR_JSON_SYNTAX);
+ }
+ else {
+ if (ch != '[') exception(ERR_PROTOCOL);
+ if (peek_stream(&c->inp) == ']') {
+ read_stream(&c->inp);
+ }
+ else {
+ for (;;) {
+ int ch;
+ char id[256];
+ BreakpointInfo * bp;
+ json_read_string(&c->inp, id, sizeof(id));
+ bp = find_breakpoint(id);
+ if (bp != NULL && !list_is_empty(&bp->link_clients) && bp->enabled) {
+ bp->enabled = 0;
+ set_breakpoint_attribute(bp, BREAKPOINT_ENABLED, "false");
+ replant_breakpoint(bp);
+ send_event_context_changed(bp);
+ }
+ ch = read_stream(&c->inp);
+ if (ch == ',') continue;
+ if (ch == ']') break;
+ exception(ERR_JSON_SYNTAX);
+ }
+ }
+ }
+ if (read_stream(&c->inp) != 0) exception(ERR_JSON_SYNTAX);
+ if (read_stream(&c->inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
+
+ write_stringz(&c->out, "R");
+ write_stringz(&c->out, token);
+ write_errno(&c->out, 0);
+ write_stream(&c->out, MARKER_EOM);
+}
+
+static void command_bp_remove(char * token, Channel * c) {
+ int ch = read_stream(&c->inp);
+ if (ch == 'n') {
+ if (read_stream(&c->inp) != 'u') exception(ERR_JSON_SYNTAX);
+ if (read_stream(&c->inp) != 'l') exception(ERR_JSON_SYNTAX);
+ if (read_stream(&c->inp) != 'l') exception(ERR_JSON_SYNTAX);
+ }
+ else {
+ if (ch != '[') exception(ERR_PROTOCOL);
+ if (peek_stream(&c->inp) == ']') {
+ read_stream(&c->inp);
+ }
+ else {
+ for (;;) {
+ int ch;
+ char id[256];
+ BreakpointRef * br;
+ json_read_string(&c->inp, id, sizeof(id));
+ br = find_breakpoint_ref(find_breakpoint(id), c);
+ if (br != NULL) remove_ref(c, br);
+ ch = read_stream(&c->inp);
+ if (ch == ',') continue;
+ if (ch == ']') break;
+ exception(ERR_JSON_SYNTAX);
+ }
+ }
+ }
+ if (read_stream(&c->inp) != 0) exception(ERR_JSON_SYNTAX);
+ if (read_stream(&c->inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
+
+ write_stringz(&c->out, "R");
+ write_stringz(&c->out, token);
+ write_errno(&c->out, 0);
+ write_stream(&c->out, MARKER_EOM);
+}
+
+static void command_get_capabilities(char * token, Channel * c) {
+ char id[256];
+ Context * ctx;
+
+ 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);
+
+ write_stringz(&c->out, "R");
+ write_stringz(&c->out, token);
+ write_errno(&c->out, 0);
+
+ write_stream(&c->out, '{');
+ json_write_string(&c->out, "ID");
+ write_stream(&c->out, ':');
+ json_write_string(&c->out, id);
+ write_stream(&c->out, ',');
+ json_write_string(&c->out, "Location");
+ write_stream(&c->out, ':');
+ json_write_boolean(&c->out, 1);
+ write_stream(&c->out, ',');
+ json_write_string(&c->out, "FileLine");
+ write_stream(&c->out, ':');
+ json_write_boolean(&c->out, ENABLE_LineNumbers);
+ write_stream(&c->out, ',');
+ json_write_string(&c->out, "FileMapping");
+ write_stream(&c->out, ':');
+ json_write_boolean(&c->out, SERVICE_PathMap);
+ write_stream(&c->out, ',');
+ json_write_string(&c->out, "IgnoreCount");
+ write_stream(&c->out, ':');
+ json_write_boolean(&c->out, 1);
+ write_stream(&c->out, ',');
+ json_write_string(&c->out, "Condition");
+ write_stream(&c->out, ':');
+ json_write_boolean(&c->out, 1);
+ if (ctx != NULL) {
+ int md = CTX_BP_ACCESS_INSTRUCTION;
+ md |= context_get_supported_bp_access_types(ctx);
+ write_stream(&c->out, ',');
+ json_write_string(&c->out, "AccessMode");
+ write_stream(&c->out, ':');
+ json_write_long(&c->out, md);
+ }
+ write_stream(&c->out, ',');
+ json_write_string(&c->out, "ContextIds");
+ write_stream(&c->out, ':');
+ json_write_boolean(&c->out, 1);
+ write_stream(&c->out, ',');
+ json_write_string(&c->out, "ContextNames");
+ write_stream(&c->out, ':');
+ json_write_boolean(&c->out, 1);
+ write_stream(&c->out, ',');
+ json_write_string(&c->out, "StopGroup");
+ write_stream(&c->out, ':');
+ json_write_boolean(&c->out, 1);
+ write_stream(&c->out, ',');
+ json_write_string(&c->out, "ClientData");
+ write_stream(&c->out, ':');
+ json_write_boolean(&c->out, 1);
+ write_stream(&c->out, '}');
+ write_stream(&c->out, 0);
+
+ write_stream(&c->out, MARKER_EOM);
+}
+
+void add_breakpoint_event_listener(BreakpointsEventListener * listener, void * args) {
+ if (listener_cnt >= listener_max) {
+ listener_max += 8;
+ listeners = (Listener *)loc_realloc(listeners, listener_max * sizeof(Listener));
+ }
+ listeners[listener_cnt].listener = listener;
+ listeners[listener_cnt].args = args;
+ listener_cnt++;
+}
+
+void rem_breakpoint_event_listener(BreakpointsEventListener * listener) {
+ unsigned i = 0;
+ while (i < listener_cnt) {
+ if (listeners[i++].listener == listener) {
+ while (i < listener_cnt) {
+ listeners[i - 1] = listeners[i];
+ i++;
+ }
+ listener_cnt--;
+ break;
+ }
+ }
+}
+
+void iterate_breakpoints(IterateBreakpointsCallBack * callback, void * args) {
+ LINK * l = breakpoints.next;
+ while (l != &breakpoints) {
+ BreakpointInfo * bp = link_all2bp(l);
+ l = l->next;
+ callback(bp, args);
+ }
+}
+
+BreakpointAttribute * get_breakpoint_attributes(BreakpointInfo * bp) {
+ return bp->attrs;
+}
+
+BreakpointInfo * create_breakpoint(BreakpointAttribute * attrs) {
+ return add_breakpoint(NULL, attrs);
+}
+
+void change_breakpoint_attributes(BreakpointInfo * bp, BreakpointAttribute * attrs) {
+ int chng = set_breakpoint_attributes(bp, attrs);
+ assert(!list_is_empty(&bp->link_clients));
+ if (chng) {
+ replant_breakpoint(bp);
+ send_event_context_changed(bp);
+ }
+}
+
+void delete_breakpoint(BreakpointInfo * bp) {
+ BreakpointRef * br = find_breakpoint_ref(bp, NULL);
+ assert(br != NULL && br->channel == NULL);
+ remove_ref(NULL, br);
+}
+
+void iterate_context_breakpoint_links(Context * ctx, ContextBreakpoint * cb, IterateCBLinksCallBack * callback, void * args) {
+ int i;
+ Context * grp = context_get_group(ctx, CONTEXT_GROUP_BREAKPOINT);
+ BreakInstruction * bi = (BreakInstruction *)((char *)cb - offsetof(BreakInstruction, cb));
+ for (i = 0; i < bi->ref_cnt; i++) {
+ if (bi->refs[i].ctx == grp) callback(bi->refs[i].bp, args);
+ }
+}
+
+int is_breakpoint_address(Context * ctx, ContextAddress address) {
+ Context * mem = NULL;
+ ContextAddress mem_addr = 0;
+ BreakInstruction * bi = NULL;
+ if (context_get_canonical_addr(ctx, address, &mem, &mem_addr, NULL, NULL) < 0) return 0;
+ bi = find_instruction(mem, 0, mem_addr);
+ return bi != NULL && bi->planted;
+}
+
+void evaluate_breakpoint(Context * ctx) {
+ int i;
+ int need_to_post = 0;
+ Context * mem = NULL;
+ ContextAddress mem_addr = 0;
+ BreakInstruction * bi = NULL;
+ EvaluationRequest * req = create_evaluation_request(ctx);
+ Context * grp = context_get_group(ctx, CONTEXT_GROUP_BREAKPOINT);
+
+ assert(context_has_state(ctx));
+ assert(ctx->stopped);
+ assert(ctx->stopped_by_bp || ctx->stopped_by_cb);
+ assert(ctx->exited == 0);
+ assert(EXT(ctx)->bp_ids == NULL);
+
+ if (ctx->stopped_by_bp) {
+ if (context_get_canonical_addr(ctx, get_regs_PC(ctx), &mem, &mem_addr, NULL, NULL) < 0) return;
+ bi = find_instruction(mem, 0, mem_addr);
+ if (bi != NULL && bi->planted) {
+ assert(bi->valid);
+ for (i = 0; i < bi->ref_cnt; i++) {
+ if (bi->refs[i].ctx == grp) {
+ BreakpointInfo * bp = bi->refs[i].bp;
+ ConditionEvaluationRequest * c = add_condition_evaluation_request(req, bp);
+ if (c == NULL) continue;
+ if (need_to_post) continue;
+ if (is_disabled(bp)) continue;
+ if (bp->condition != NULL || bp->stop_group != NULL) {
+ need_to_post = 1;
+ continue;
+ }
+ if (!check_context_ids_condition(bp, ctx)) continue;
+ c->condition_ok = 1;
+ }
+ }
+ }
+ }
+ if (ctx->stopped_by_cb) {
+ int j;
+ assert(ctx->stopped_by_cb[0] != NULL);
+ for (j = 0; ctx->stopped_by_cb[j]; j++) {
+ bi = (BreakInstruction *)((char *)ctx->stopped_by_cb[j] - offsetof(BreakInstruction, cb));
+ assert(bi->planted);
+ for (i = 0; i < bi->ref_cnt; i++) {
+ if (bi->refs[i].ctx == grp) {
+ BreakpointInfo * bp = bi->refs[i].bp;
+ ConditionEvaluationRequest * c = add_condition_evaluation_request(req, bp);
+ if (c == NULL) continue;
+ if (need_to_post) continue;
+ if (is_disabled(bp)) continue;
+ if (bp->condition != NULL || bp->stop_group != NULL) {
+ need_to_post = 1;
+ continue;
+ }
+ if (!check_context_ids_condition(bp, ctx)) continue;
+ c->condition_ok = 1;
+ }
+ }
+ }
+ }
+
+ if (need_to_post) {
+ post_evaluation_request(req);
+ }
+ else {
+ done_condition_evaluation(req);
+ req->bp_cnt = 0;
+ }
+}
+
+char ** get_context_breakpoint_ids(Context * ctx) {
+ return EXT(ctx)->bp_ids;
+}
+
+static void safe_skip_breakpoint(void * arg);
+
+static void safe_restore_breakpoint(void * arg) {
+ Context * ctx = (Context *)arg;
+ ContextExtensionBP * ext = EXT(ctx);
+ BreakInstruction * bi = ext->stepping_over_bp;
+
+ assert(bi->stepping_over_bp > 0);
+ assert(find_instruction(bi->cb.ctx, 0, bi->cb.address) == bi);
+ if (!ctx->exiting && ctx->stopped && !ctx->stopped_by_exception && get_regs_PC(ctx) == bi->cb.address) {
+ if (ext->step_over_bp_cnt < 100) {
+ ext->step_over_bp_cnt++;
+ safe_skip_breakpoint(arg);
+ return;
+ }
+ trace(LOG_ALWAYS, "Skip breakpoint error: wrong PC %#lx", get_regs_PC(ctx));
+ }
+ ext->stepping_over_bp = NULL;
+ ext->step_over_bp_cnt = 0;
+ bi->stepping_over_bp--;
+ if (bi->stepping_over_bp == 0) {
+ if (generation_done != generation_posted) {
+ bi->valid = 0;
+ }
+ else if (!ctx->exited && bi->ref_cnt > 0 && !bi->planted) {
+ plant_instruction(bi);
+ }
+ }
+ context_unlock(ctx);
+}
+
+static void safe_skip_breakpoint(void * arg) {
+ Context * ctx = (Context *)arg;
+ ContextExtensionBP * ext = EXT(ctx);
+ BreakInstruction * bi = ext->stepping_over_bp;
+ int error = 0;
+
+ assert(bi != NULL);
+ assert(bi->stepping_over_bp > 0);
+ assert(find_instruction(bi->cb.ctx, 0, bi->cb.address) == bi);
+
+ post_safe_event(ctx, safe_restore_breakpoint, ctx);
+
+ if (ctx->exited || ctx->exiting) return;
+
+ assert(ctx->stopped);
+ assert(bi->cb.address == get_regs_PC(ctx));
+
+ if (bi->planted) remove_instruction(bi);
+ if (bi->planting_error) error = set_error_report_errno(bi->planting_error);
+ if (error == 0 && safe_context_single_step(ctx) < 0) error = errno;
+ if (error) {
+ error = set_errno(error, "Cannot step over breakpoint");
+ ctx->signal = 0;
+ ctx->stopped = 1;
+ ctx->stopped_by_bp = 0;
+ ctx->stopped_by_cb = NULL;
+ ctx->stopped_by_exception = 1;
+ ctx->pending_intercept = 1;
+ loc_free(ctx->exception_description);
+ ctx->exception_description = loc_strdup(errno_to_str(error));
+ send_context_changed_event(ctx);
+ }
+}
+
+/*
+ * When a context is stopped by breakpoint, it is necessary to disable
+ * the breakpoint temporarily before the context can be resumed.
+ * This function function removes break instruction, then does single step
+ * over breakpoint location, then restores break intruction.
+ * Return: 0 if it is OK to resume context from current state,
+ * return 1 if context needs to step over a breakpoint.
+ */
+int skip_breakpoint(Context * ctx, int single_step) {
+ ContextExtensionBP * ext = EXT(ctx);
+ Context * mem = NULL;
+ ContextAddress mem_addr = 0;
+ BreakInstruction * bi;
+
+ assert(ctx->stopped);
+ assert(!ctx->exited);
+ assert(single_step || ext->stepping_over_bp == NULL);
+
+ if (ext->stepping_over_bp != NULL) return 0;
+ if (ctx->exited || ctx->exiting || !ctx->stopped_by_bp) return 0;
+
+ if (context_get_canonical_addr(ctx, get_regs_PC(ctx), &mem, &mem_addr, NULL, NULL) < 0) return -1;
+ bi = find_instruction(mem, 0, mem_addr);
+ if (bi == NULL || bi->planting_error) return 0;
+ bi->stepping_over_bp++;
+ ext->stepping_over_bp = bi;
+ ext->step_over_bp_cnt = 1;
+ assert(bi->stepping_over_bp > 0);
+ context_lock(ctx);
+ post_safe_event(ctx, safe_skip_breakpoint, ctx);
+ return 1;
+}
+
+BreakpointInfo * create_eventpoint(const char * location, Context * ctx, EventPointCallBack * callback, void * callback_args) {
+ static const char * attr_list[] = { BREAKPOINT_ENABLED, BREAKPOINT_LOCATION };
+ BreakpointInfo * bp = (BreakpointInfo *)loc_alloc_zero(sizeof(BreakpointInfo));
+ BreakpointAttribute ** ref = &bp->attrs;
+ unsigned i;
+
+ bp->client_cnt = 1;
+ bp->enabled = 1;
+ if (location != NULL) bp->location = loc_strdup(location);
+ if (ctx != NULL) context_lock(bp->ctx = ctx);
+
+ /* Create attributes to allow get_breakpoint_attributes() and change_breakpoint_attributes() calls */
+ for (i = 0; i < sizeof(attr_list) / sizeof(char *); i++) {
+ ByteArrayOutputStream buf;
+ BreakpointAttribute * attr = (BreakpointAttribute *)loc_alloc_zero(sizeof(BreakpointAttribute));
+ OutputStream * out = create_byte_array_output_stream(&buf);
+ attr->name = loc_strdup(attr_list[i]);
+ switch (i) {
+ case 0:
+ json_write_boolean(out, bp->enabled);
+ break;
+ case 1:
+ json_write_string(out, bp->location);
+ break;
+ }
+ write_stream(out, 0);
+ get_byte_array_output_stream_data(&buf, &attr->value, NULL);
+ *ref = attr; ref = &attr->next;
+ }
+
+ bp->event_callback = callback;
+ bp->event_callback_args = callback_args;
+ list_init(&bp->link_clients);
+ assert(breakpoints.next != NULL);
+ list_add_last(&bp->link_all, &breakpoints);
+ replant_breakpoint(bp);
+ return bp;
+}
+
+void destroy_eventpoint(BreakpointInfo * bp) {
+ assert(bp->id[0] == 0);
+ assert(bp->client_cnt == 1);
+ assert(list_is_empty(&bp->link_clients));
+ bp->client_cnt = 0;
+ replant_breakpoint(bp);
+}
+
+static void event_context_created_or_exited(Context * ctx, void * args) {
+ post_location_evaluation_request(ctx, NULL);
+}
+
+static void event_context_changed(Context * ctx, void * args) {
+ if (ctx->mem_access && context_get_group(ctx, CONTEXT_GROUP_PROCESS) == ctx) {
+ /* If the context is a memory space, we need to update
+ * breakpoints on all members of the group */
+ LINK * l = context_root.next;
+ while (l != &context_root) {
+ Context * x = ctxl2ctxp(l);
+ l = l->next;
+ if (x->exited) continue;
+ if (context_get_group(x, CONTEXT_GROUP_PROCESS) != ctx) continue;
+ post_location_evaluation_request(x, NULL);
+ }
+ }
+ else {
+ post_location_evaluation_request(ctx, NULL);
+ }
+}
+
+static void event_context_started(Context * ctx, void * args) {
+ ContextExtensionBP * ext = EXT(ctx);
+ if (ext->bp_ids != NULL) {
+ loc_free(ext->bp_ids);
+ ext->bp_ids = NULL;
+ }
+}
+
+static void event_context_disposed(Context * ctx, void * args) {
+ ContextExtensionBP * ext = EXT(ctx);
+ EvaluationRequest * req = ext->req;
+ if (req != NULL) {
+ loc_free(req->bp_arr);
+ loc_free(req);
+ ext->req = NULL;
+ }
+ if (ext->bp_ids != NULL) {
+ loc_free(ext->bp_ids);
+ ext->bp_ids = NULL;
+ }
+}
+
+#if SERVICE_MemoryMap
+static void event_code_unmapped(Context * ctx, ContextAddress addr, ContextAddress size, void * args) {
+ /* Unmapping a code section unplants all breakpoint instructions in that section as side effect.
+ * This function udates service data structure to reflect that.
+ */
+ int cnt = 0;
+ while (size > 0) {
+ ContextAddress sz = size;
+ LINK * l = instructions.next;
+ Context * mem = NULL;
+ ContextAddress mem_addr = 0;
+ ContextAddress mem_base = 0;
+ ContextAddress mem_size = 0;
+ if (context_get_canonical_addr(ctx, addr, &mem, &mem_addr, &mem_base, &mem_size) < 0) break;
+ if (mem_base + mem_size - mem_addr < sz) sz = mem_base + mem_size - mem_addr;
+ while (l != &instructions) {
+ int i;
+ BreakInstruction * bi = link_all2bi(l);
+ l = l->next;
+ if (bi->cb.ctx != mem) continue;
+ if (!bi->planted) continue;
+ if (bi->cb.address < mem_addr || bi->cb.address >= mem_addr + sz) continue;
+ for (i = 0; i < bi->ref_cnt; i++) {
+ bi->refs[i].bp->status_changed = 1;
+ cnt++;
+ }
+ bi->planted = 0;
+ }
+ addr += sz;
+ size -= sz;
+ }
+ if (cnt > 0 && generation_done == generation_active) notify_breakpoints_status();
+}
+#endif
+
+#if SERVICE_PathMap
+static void event_path_map_changed(Channel * c, void * args) {
+ unsigned hash = (unsigned)(uintptr_t)c / 16 % INP2BR_HASH_SIZE;
+ LINK * l = inp2br[hash].next;
+ while (l != &inp2br[hash]) {
+ BreakpointRef * br = link_inp2br(l);
+ l = l->next;
+ if (br->channel == c && br->bp->file != NULL) replant_breakpoint(br->bp);
+ }
+}
+#endif
+
+static void channel_close_listener(Channel * c) {
+ delete_breakpoint_refs(c);
+}
+
+void ini_breakpoints_service(Protocol * proto, TCFBroadcastGroup * bcg) {
+ int i;
+ broadcast_group = bcg;
+
+ {
+ static ContextEventListener listener = {
+ event_context_created_or_exited,
+ event_context_created_or_exited,
+ NULL,
+ event_context_started,
+ event_context_changed,
+ event_context_disposed
+ };
+ add_context_event_listener(&listener, NULL);
+ }
+#if SERVICE_MemoryMap
+ {
+ static MemoryMapEventListener listener = {
+ event_context_changed,
+ event_code_unmapped,
+ event_context_changed,
+ event_context_changed,
+ };
+ add_memory_map_event_listener(&listener, NULL);
+ }
+#endif
+#if SERVICE_PathMap
+ {
+ static PathMapEventListener listener = {
+ event_path_map_changed,
+ };
+ add_path_map_event_listener(&listener, NULL);
+ }
+#endif
+ list_init(&breakpoints);
+ list_init(&instructions);
+ list_init(&evaluations_posted);
+ list_init(&evaluations_active);
+ for (i = 0; i < ADDR2INSTR_HASH_SIZE; i++) list_init(addr2instr + i);
+ for (i = 0; i < ID2BP_HASH_SIZE; i++) list_init(id2bp + i);
+ for (i = 0; i < INP2BR_HASH_SIZE; i++) list_init(inp2br + i);
+ add_channel_close_listener(channel_close_listener);
+ add_command_handler(proto, BREAKPOINTS, "set", command_ini_bps);
+ add_command_handler(proto, BREAKPOINTS, "add", command_bp_add);
+ add_command_handler(proto, BREAKPOINTS, "change", command_bp_change);
+ add_command_handler(proto, BREAKPOINTS, "enable", command_bp_enable);
+ add_command_handler(proto, BREAKPOINTS, "disable", command_bp_disable);
+ add_command_handler(proto, BREAKPOINTS, "remove", command_bp_remove);
+ add_command_handler(proto, BREAKPOINTS, "getIDs", command_get_bp_ids);
+ add_command_handler(proto, BREAKPOINTS, "getProperties", command_get_properties);
+ add_command_handler(proto, BREAKPOINTS, "getStatus", command_get_status);
+ add_command_handler(proto, BREAKPOINTS, "getCapabilities", command_get_capabilities);
+ context_extension_offset = context_extension(sizeof(ContextExtensionBP));
+}
+
+#endif /* SERVICE_Breakpoints */
diff --git a/agent/tcf/services/breakpoints.h b/agent/tcf/services/breakpoints.h
new file mode 100644
index 00000000..4635d2dc
--- /dev/null
+++ b/agent/tcf/services/breakpoints.h
@@ -0,0 +1,203 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+
+/*
+ * This module implements Breakpoints service.
+ * The service maintains a list of breakpoints.
+ * Each breakpoint consists of one or more conditions that determine
+ * when a program's execution should be interrupted.
+ */
+
+#ifndef D_breakpoints
+#define D_breakpoints
+
+#include <framework/context.h>
+#include <framework/protocol.h>
+
+typedef struct BreakpointInfo BreakpointInfo;
+typedef struct BreakpointAttribute BreakpointAttribute;
+
+struct BreakpointAttribute {
+ BreakpointAttribute * next;
+ char * name; /* Attribute name */
+ char * value; /* Attribute value as JSON string */
+};
+
+#if SERVICE_Breakpoints
+
+/*
+ * Breakpoint attribute names.
+ * Clients may define additional attributes.
+ */
+#define BREAKPOINT_ID "ID"
+#define BREAKPOINT_ENABLED "Enabled"
+#define BREAKPOINT_TYPE "BreakpointType"
+#define BREAKPOINT_CONTEXTNAMES "ContextNames"
+#define BREAKPOINT_CONTEXTIDS "ContextIds"
+#define BREAKPOINT_EXECUTABLEPATHS "ExecPaths"
+#define BREAKPOINT_LOCATION "Location"
+#define BREAKPOINT_SIZE "Size"
+#define BREAKPOINT_ACCESSMODE "AccessMode"
+#define BREAKPOINT_FILE "File"
+#define BREAKPOINT_LINE "Line"
+#define BREAKPOINT_COLUMN "Column"
+#define BREAKPOINT_PATTERN "MaskValue"
+#define BREAKPOINT_MASK "Mask"
+#define BREAKPOINT_STOP_GROUP "StopGroup"
+#define BREAKPOINT_IGNORECOUNT "IgnoreCount"
+#define BREAKPOINT_TIME "Time"
+#define BREAKPOINT_SCALE "TimeScale"
+#define BREAKPOINT_UNITS "TimeUnits"
+#define BREAKPOINT_CONDITION "Condition"
+#define BREAKPOINT_TEMPORARY "Temporary"
+#define BREAKPOINT_CLIENT_DATA "ClientData"
+
+
+/* Breakpoints event listener */
+typedef struct BreakpointsEventListener {
+ void (*breakpoint_created)(BreakpointInfo *, void *);
+ void (*breakpoint_changed)(BreakpointInfo *, void *);
+ void (*breakpoint_deleted)(BreakpointInfo *, void *);
+ void (*breakpoint_status_changed)(BreakpointInfo *, void *);
+} BreakpointsEventListener;
+
+/*
+ * Add a listener for Breakpoints service events.
+ */
+extern void add_breakpoint_event_listener(BreakpointsEventListener * listener, void * args);
+
+/*
+ * Remove a listener of Breakpoints service events.
+ */
+extern void rem_breakpoint_event_listener(BreakpointsEventListener * listener);
+
+/*
+ * Iterate all breakpoints known to the Breakpoints service,
+ * including breakpoints that are created by other (remote) clients.
+ */
+typedef void IterateBreakpointsCallBack(BreakpointInfo *, void *);
+extern void iterate_breakpoints(IterateBreakpointsCallBack * callback, void * args);
+
+/*
+ * Get breakpoint attributes.
+ */
+extern BreakpointAttribute * get_breakpoint_attributes(BreakpointInfo * bp);
+
+/*
+ * Create new breakpoint with given attributes.
+ * Attributes must include, at least, BREAKPOINT_ID.
+ * If a breakpoint with such ID already exists, it will be modified to match
+ * new attributes instead of creating a new one.
+ * Caller should allocate attributes using myalloc.h functions.
+ * Breakpoints service will free attributes memory using loc_free().
+ */
+extern BreakpointInfo * create_breakpoint(BreakpointAttribute * attrs);
+
+/*
+ * Change breakpoint attributes to given attributes.
+ * Caller should allocate attributes using myalloc.h functions.
+ * Breakpoints service will free attributes memory using loc_free().
+ * The function compares existing attributes with new ones,
+ * and calls listeners only if attributes are different.
+ */
+extern void change_breakpoint_attributes(BreakpointInfo * bp, BreakpointAttribute * attrs);
+
+/*
+ * Delete a breakpoint.
+ * If other (remote) client also created a breakpoint with same ID,
+ * the breakpoint will be deleted when all clients have requested it to be deleted.
+ */
+extern void delete_breakpoint(BreakpointInfo * bp);
+
+/*
+ * Iterate all breakpoints that are linked to context breakpoint 'cb' in the breakpoint address space
+ * associated with executable context 'ctx'. Breakpoint address space is the context returned by
+ * context_get_group(ctx, CONTEXT_GROUP_BREAKPOINT).
+ * Single 'cb' can be linked to multiple breakpoints if those breakpoint locations are evaluated
+ * to same address in same address space. Single breakpoint can be linked to multiple CBs if the
+ * breakpoint scope spawns multiple address spaces.
+ */
+typedef void IterateCBLinksCallBack(BreakpointInfo *, void *);
+extern void iterate_context_breakpoint_links(Context * ctx, ContextBreakpoint * cb, IterateCBLinksCallBack * callback, void * args);
+
+/*
+ * The function is called from context.c every time a context is stopped by a breakpoint.
+ * The function evaluates breakpoint condition and calls suspend_debug_context() if the condition is true.
+ */
+extern void evaluate_breakpoint(Context * ctx);
+
+/*
+ * Return NULL-terminated array of breakpoint IDs if the context is stopped by breakpoint.
+ * Otherwise return NULL.
+ */
+extern char ** get_context_breakpoint_ids(Context * ctx);
+
+/*
+ * When a context is stopped by breakpoint, it is necessary to disable
+ * the breakpoint temporarily before the context can be resumed.
+ * This function function removes break instruction, then does single step
+ * over breakpoint location, then restores break intruction.
+ * Return 0 if it is OK to resume context from current state,
+ * return 1 if context needs to step over a breakpoint.
+ */
+extern int skip_breakpoint(Context * ctx, int single_step);
+
+/* Return 1 if break instruction is planted at given address in the context memory */
+extern int is_breakpoint_address(Context * ctx, ContextAddress address);
+
+/* Clone all planted breakpoints when a process forks */
+extern void clone_breakpoints_on_process_fork(Context * parent, Context * child);
+
+/* Unplant all breakpoints in a process (e.g. before detaching) */
+extern void unplant_breakpoints(Context * ctx);
+
+/*
+ * Check if memory data buffer contans planted break instructions and remove them.
+ * Return -1 and set errno if the check cannot be done.
+ */
+extern int check_breakpoints_on_memory_read(Context * ctx, ContextAddress address, void * buf, size_t size);
+
+/*
+ * Check if data is about to be written over planted break instructions and adjust the data and breakpoint backing storage
+ * Return -1 and set errno if the check cannot be done.
+ * Return 0 on success.
+ */
+extern int check_breakpoints_on_memory_write(Context * ctx, ContextAddress address, void * buf, size_t size);
+
+/* Evenpoint callback. It is called when context is suspended by eventpoint, right before "context_stopped" event */
+typedef void EventPointCallBack(Context *, void *);
+
+/* Create, plant and return eventpoint. Eventpoints are breakpoints that are created by agent to control execution of debugee.
+ * Eventpoint are not exposed through "Breakpoints" TCF service, they are handled by agent itself. */
+extern BreakpointInfo * create_eventpoint(const char * location, Context * ctx, EventPointCallBack * callback, void * callback_args);
+
+/* Unplant and destroy eventpoint */
+extern void destroy_eventpoint(BreakpointInfo * eventpoint);
+
+#else /* SERVICE_Breakpoints */
+
+#define skip_breakpoint(ctx, single_step) 0
+#define is_breakpoint_address(ctx, address) 0
+#define clone_breakpoints_on_process_fork(parent, child) 0
+#define unplant_breakpoints(ctx) 0
+#define check_breakpoints_on_memory_read(ctx, address, buf, size) 0
+#define check_breakpoints_on_memory_write(ctx, address, buf, size) 0
+#define create_eventpoint(location, ctx, callback, callback_args) 0
+
+#endif /* SERVICE_Breakpoints */
+
+extern void ini_breakpoints_service(Protocol *, TCFBroadcastGroup *);
+
+#endif /* D_breakpoints */
diff --git a/agent/tcf/services/diagnostics.c b/agent/tcf/services/diagnostics.c
new file mode 100644
index 00000000..5ad3d41e
--- /dev/null
+++ b/agent/tcf/services/diagnostics.c
@@ -0,0 +1,451 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2011 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
+ *******************************************************************************/
+
+/*
+ * Diagnostics service.
+ * This service is used for framework and agents testing.
+ */
+
+#include <config.h>
+#include <signal.h>
+#include <assert.h>
+#include <stdio.h>
+#include <string.h>
+#include <framework/protocol.h>
+#include <framework/json.h>
+#include <framework/exceptions.h>
+#include <framework/context.h>
+#include <framework/myalloc.h>
+#include <framework/cache.h>
+#if ENABLE_Symbols
+# include <services/symbols.h>
+#endif
+#if SERVICE_Streams
+# include <services/streamsservice.h>
+#endif
+#if ENABLE_RCBP_TEST
+# include <main/test.h>
+# include <services/runctrl.h>
+#endif
+#include <services/diagnostics.h>
+
+static const char * DIAGNOSTICS = "Diagnostics";
+
+#if ENABLE_RCBP_TEST
+
+typedef struct ContextExtensionDiag {
+ int test_process;
+ Channel * channel;
+} ContextExtensionDiag;
+
+static size_t context_extension_offset = 0;
+
+#define EXT(ctx) ((ContextExtensionDiag *)((char *)(ctx) + context_extension_offset))
+
+int is_test_process(Context * ctx) {
+#if defined(_WRS_KERNEL)
+ return 1;
+#else
+ return EXT(context_get_group(ctx, CONTEXT_GROUP_PROCESS))->test_process;
+#endif
+}
+
+static void channel_close_listener(Channel * c) {
+ LINK * l = context_root.next;
+ while (l != &context_root) {
+ Context * ctx = ctxl2ctxp(l);
+ l = l->next;
+ if (EXT(ctx)->channel == c || (ctx->creator != NULL && EXT(ctx->creator)->channel == c)) {
+ terminate_debug_context(ctx);
+ }
+ }
+ l = context_root.next;
+ while (l != &context_root) {
+ Context * ctx = ctxl2ctxp(l);
+ if (EXT(ctx)->channel == c) EXT(ctx)->channel = NULL;
+ l = l->next;
+ }
+}
+
+#endif /* ENABLE_RCBP_TEST */
+
+typedef struct RunTestDoneArgs RunTestDoneArgs;
+
+struct RunTestDoneArgs {
+ Channel * c;
+ Context * ctx;
+ char token[256];
+};
+
+static void command_echo(char * token, Channel * c) {
+ char str[0x1000];
+ int len = json_read_string(&c->inp, str, sizeof(str));
+ if (len >= (int)sizeof(str)) exception(ERR_JSON_SYNTAX);
+ if (read_stream(&c->inp) != 0) exception(ERR_JSON_SYNTAX);
+ if (read_stream(&c->inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
+ write_stringz(&c->out, "R");
+ write_stringz(&c->out, token);
+ json_write_string_len(&c->out, str, len);
+ write_stream(&c->out, 0);
+ write_stream(&c->out, MARKER_EOM);
+}
+
+static void command_echo_fp(char * token, Channel * c) {
+ double x = json_read_double(&c->inp);
+ if (read_stream(&c->inp) != 0) exception(ERR_JSON_SYNTAX);
+ if (read_stream(&c->inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
+ write_stringz(&c->out, "R");
+ write_stringz(&c->out, token);
+ json_write_double(&c->out, x);
+ write_stream(&c->out, 0);
+ write_stream(&c->out, MARKER_EOM);
+}
+
+static void command_echo_err(char * token, Channel * c) {
+ int no = read_errno(&c->inp);
+ if (read_stream(&c->inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
+ write_stringz(&c->out, "R");
+ write_stringz(&c->out, token);
+ write_errno(&c->out, no);
+ json_write_string(&c->out, errno_to_str(no));
+ write_stream(&c->out, 0);
+ write_stream(&c->out, MARKER_EOM);
+}
+
+static void command_get_test_list(char * token, Channel * c) {
+ const char * arr = "[]";
+ if (read_stream(&c->inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
+ write_stringz(&c->out, "R");
+ write_stringz(&c->out, token);
+ write_errno(&c->out, 0);
+#if ENABLE_RCBP_TEST
+ arr = "[\"RCBP1\"]";
+#endif
+ write_stringz(&c->out, arr);
+ write_stream(&c->out, MARKER_EOM);
+}
+
+#if ENABLE_RCBP_TEST
+static void run_test_done(int error, Context * ctx, void * arg) {
+ RunTestDoneArgs * data = (RunTestDoneArgs *)arg;
+ Channel * c = data->c;
+
+ if (ctx != NULL) {
+ EXT(context_get_group(ctx, CONTEXT_GROUP_PROCESS))->test_process = 1;
+ EXT(ctx)->channel = c;
+ }
+ if (!is_channel_closed(c)) {
+ write_stringz(&c->out, "R");
+ write_stringz(&c->out, data->token);
+ write_errno(&c->out, error);
+ json_write_string(&c->out, ctx ? ctx->id : NULL);
+ write_stream(&c->out, 0);
+ write_stream(&c->out, MARKER_EOM);
+ }
+ else if (ctx != NULL) {
+ terminate_debug_context(ctx);
+ }
+ channel_unlock(c);
+ loc_free(data);
+}
+#endif
+
+static void command_run_test(char * token, Channel * c) {
+ int err = 0;
+ char id[256];
+
+ 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);
+
+ if (strcmp(id, "RCBP1") == 0) {
+#if ENABLE_RCBP_TEST
+ RunTestDoneArgs * data = (RunTestDoneArgs *)loc_alloc_zero(sizeof(RunTestDoneArgs));
+ data->c = c;
+ strcpy(data->token, token);
+ if (run_test_process(run_test_done, data) == 0) {
+ channel_lock(c);
+ return;
+ }
+ err = errno;
+ loc_free(data);
+#else
+ err = EINVAL;
+#endif
+ }
+ else {
+ err = EINVAL;
+ }
+ write_stringz(&c->out, "R");
+ write_stringz(&c->out, token);
+ write_errno(&c->out, err);
+ json_write_string(&c->out, NULL);
+ write_stream(&c->out, 0);
+ write_stream(&c->out, MARKER_EOM);
+}
+
+static void command_cancel_test(char * token, Channel * c) {
+ char id[256];
+ int err = 0;
+
+ 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);
+
+#if ENABLE_RCBP_TEST
+ if (terminate_debug_context(id2ctx(id)) != 0) err = errno;
+#else
+ err = ERR_UNSUPPORTED;
+#endif
+
+ write_stringz(&c->out, "R");
+ write_stringz(&c->out, token);
+ write_errno(&c->out, err);
+ write_stream(&c->out, MARKER_EOM);
+}
+
+static void write_symbol(Channel * c, ContextAddress address) {
+ if (address == 0) {
+ write_stringz(&c->out, "null");
+ }
+ else {
+ write_stream(&c->out, '{');
+ json_write_string(&c->out, "Abs");
+ write_stream(&c->out, ':');
+ json_write_boolean(&c->out, 1);
+ write_stream(&c->out, ',');
+ json_write_string(&c->out, "Value");
+ write_stream(&c->out, ':');
+ json_write_uint64(&c->out, address);
+ write_stream(&c->out, '}');
+ write_stream(&c->out, 0);
+ }
+}
+
+#if ENABLE_Symbols
+
+typedef struct GetSymbolArgs {
+ char token[256];
+ Context * ctx;
+ char * name;
+} GetSymbolArgs;
+
+static void get_symbol_cache_client(void * x) {
+ GetSymbolArgs * args = (GetSymbolArgs *)x;
+ Channel * c = cache_channel();
+ Context * ctx = args->ctx;
+ Symbol * sym = NULL;
+ ContextAddress addr = 0;
+ int error = 0;
+
+ if (ctx->exited) {
+ error = ERR_ALREADY_EXITED;
+ }
+ else if (find_symbol_by_name(ctx, STACK_NO_FRAME, 0, args->name, &sym) < 0) {
+ error = errno;
+ }
+ else if (get_symbol_address(sym, &addr) < 0) {
+ error = errno;
+ }
+ cache_exit();
+
+ write_stringz(&c->out, "R");
+ write_stringz(&c->out, args->token);
+ write_errno(&c->out, error);
+ write_symbol(c, addr);
+ write_stream(&c->out, MARKER_EOM);
+
+ context_unlock(ctx);
+ loc_free(args->name);
+}
+
+#endif /* ENABLE_Symbols */
+
+static void command_get_symbol(char * token, Channel * c) {
+ char id[256];
+ char * name = NULL;
+ int error = 0;
+ ContextAddress addr = 0;
+
+ json_read_string(&c->inp, id, sizeof(id));
+ if (read_stream(&c->inp) != 0) exception(ERR_JSON_SYNTAX);
+ name = json_read_alloc_string(&c->inp);
+ if (read_stream(&c->inp) != 0) exception(ERR_JSON_SYNTAX);
+ if (read_stream(&c->inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
+
+#if ENABLE_DebugContext
+ {
+ Context *ctx = id2ctx(id);
+ if (ctx == NULL) {
+ error = ERR_INV_CONTEXT;
+ }
+ else if (ctx->exited) {
+ error = ERR_ALREADY_EXITED;
+ }
+ else {
+#if ENABLE_Symbols
+ GetSymbolArgs args;
+ strlcpy(args.token, token, sizeof(args.token));
+ context_lock(ctx);
+ args.ctx = ctx;
+ args.name = name;
+ cache_enter(get_symbol_cache_client, c, &args, sizeof(args));
+ return;
+#elif ENABLE_RCBP_TEST
+ void * ptr = NULL;
+ int cls = 0;
+ if (find_test_symbol(ctx, name, &ptr, &cls) < 0) error = errno;
+ addr = (ContextAddress)ptr;
+#else
+ error = ERR_UNSUPPORTED;
+#endif
+ }
+ }
+#else
+ error = ERR_UNSUPPORTED;
+#endif
+
+ write_stringz(&c->out, "R");
+ write_stringz(&c->out, token);
+ write_errno(&c->out, error);
+ write_symbol(c, addr);
+ write_stream(&c->out, MARKER_EOM);
+ loc_free(name);
+}
+
+#if SERVICE_Streams
+
+typedef struct StreamsTest {
+ VirtualStream * inp;
+ VirtualStream * out;
+ char buf[111];
+ size_t buf_pos;
+ size_t buf_len;
+ int inp_eos;
+ int out_eos;
+} StreamsTest;
+
+static void streams_test_callback(VirtualStream * stream, int event_code, void * args) {
+ StreamsTest * st = (StreamsTest *)args;
+
+ switch (event_code) {
+ case VS_EVENT_SPACE_AVAILABLE:
+ if (stream != st->out) return;
+ break;
+ case VS_EVENT_DATA_AVAILABLE:
+ if (stream != st->inp) return;
+ break;
+ }
+
+ if (st->buf_pos == st->buf_len && !st->inp_eos) {
+ st->buf_pos = st->buf_len = 0;
+ virtual_stream_get_data(st->inp, st->buf, sizeof(st->buf), &st->buf_len, &st->inp_eos);
+ }
+
+ if (st->buf_len > st->buf_pos || st->inp_eos != st->out_eos) {
+ size_t done = 0;
+ virtual_stream_add_data(st->out, st->buf + st->buf_pos, st->buf_len - st->buf_pos, &done, st->inp_eos);
+ st->buf_pos += done;
+ if (st->buf_pos == st->buf_len && st->inp_eos) st->out_eos = 1;
+ }
+
+ if (st->inp_eos && st->out_eos) loc_free(st);
+}
+
+#endif /* SERVICE_Streams */
+
+static void command_create_test_streams(char * token, Channel * c) {
+ char id_inp[256];
+ char id_out[256];
+ long buf_size0;
+ long buf_size1;
+ int err = 0;
+
+ buf_size0 = json_read_long(&c->inp);
+ if (read_stream(&c->inp) != 0) exception(ERR_JSON_SYNTAX);
+ buf_size1 = json_read_long(&c->inp);
+ if (read_stream(&c->inp) != 0) exception(ERR_JSON_SYNTAX);
+ if (read_stream(&c->inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
+
+ if (buf_size0 <= 0 || buf_size1 <= 0) err = ERR_INV_NUMBER;
+
+#if SERVICE_Streams
+ if (!err) {
+ StreamsTest * st = (StreamsTest *)loc_alloc_zero(sizeof(StreamsTest));
+ virtual_stream_create(DIAGNOSTICS, NULL, (unsigned)buf_size0,
+ VS_ENABLE_REMOTE_WRITE, streams_test_callback, st, &st->inp);
+ virtual_stream_create(DIAGNOSTICS, NULL, (unsigned)buf_size1,
+ VS_ENABLE_REMOTE_READ, streams_test_callback, st, &st->out);
+ virtual_stream_get_id(st->inp, id_inp, sizeof(id_inp));
+ virtual_stream_get_id(st->out, id_out, sizeof(id_out));
+ }
+#else
+ if (!err) err = ERR_UNSUPPORTED;
+#endif
+
+ write_stringz(&c->out, "R");
+ write_stringz(&c->out, token);
+ write_errno(&c->out, err);
+ if (err) {
+ write_stringz(&c->out, "null");
+ write_stringz(&c->out, "null");
+ }
+ else {
+ json_write_string(&c->out, id_inp);
+ write_stream(&c->out, 0);
+ json_write_string(&c->out, id_out);
+ write_stream(&c->out, 0);
+ }
+ write_stream(&c->out, MARKER_EOM);
+}
+
+static void command_dispose_test_stream(char * token, Channel * c) {
+ char id[256];
+ int err = 0;
+
+ 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);
+
+#if SERVICE_Streams
+ if (!err) {
+ VirtualStream * stream = virtual_stream_find(id);
+ if (stream == NULL) err = errno;
+ else virtual_stream_delete(stream);
+ }
+#else
+ err = ERR_UNSUPPORTED;
+#endif
+ write_stringz(&c->out, "R");
+ write_stringz(&c->out, token);
+ write_errno(&c->out, err);
+ write_stream(&c->out, MARKER_EOM);
+}
+
+void ini_diagnostics_service(Protocol * proto) {
+ add_command_handler(proto, DIAGNOSTICS, "echo", command_echo);
+ add_command_handler(proto, DIAGNOSTICS, "echoFP", command_echo_fp);
+ add_command_handler(proto, DIAGNOSTICS, "echoERR", command_echo_err);
+ add_command_handler(proto, DIAGNOSTICS, "getTestList", command_get_test_list);
+ add_command_handler(proto, DIAGNOSTICS, "runTest", command_run_test);
+ add_command_handler(proto, DIAGNOSTICS, "cancelTest", command_cancel_test);
+ add_command_handler(proto, DIAGNOSTICS, "getSymbol", command_get_symbol);
+ add_command_handler(proto, DIAGNOSTICS, "createTestStreams", command_create_test_streams);
+ add_command_handler(proto, DIAGNOSTICS, "disposeTestStream", command_dispose_test_stream);
+#if ENABLE_RCBP_TEST
+ context_extension_offset = context_extension(sizeof(ContextExtensionDiag));
+ add_channel_close_listener(channel_close_listener);
+#endif
+}
diff --git a/agent/tcf/services/diagnostics.h b/agent/tcf/services/diagnostics.h
new file mode 100644
index 00000000..0624b74b
--- /dev/null
+++ b/agent/tcf/services/diagnostics.h
@@ -0,0 +1,31 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+
+/*
+ * Diagnostics service.
+ * This service is used for framework and agents testing.
+ */
+
+#ifndef D_diagnostics
+#define D_diagnostics
+
+#include <framework/protocol.h>
+#include <framework/context.h>
+
+extern int is_test_process(Context * ctx);
+
+extern void ini_diagnostics_service(Protocol *);
+
+#endif /* D_diagnostics */
diff --git a/agent/tcf/services/discovery.c b/agent/tcf/services/discovery.c
new file mode 100644
index 00000000..432eea6e
--- /dev/null
+++ b/agent/tcf/services/discovery.c
@@ -0,0 +1,202 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+
+/*
+ * Implements auto-discovery and Locator service.
+ */
+
+#include <config.h>
+
+#include <stddef.h>
+#include <errno.h>
+#include <assert.h>
+#include <string.h>
+#include <framework/protocol.h>
+#include <framework/channel.h>
+#include <framework/proxy.h>
+#include <framework/myalloc.h>
+#include <framework/events.h>
+#include <framework/trace.h>
+#include <framework/exceptions.h>
+#include <framework/json.h>
+#include <framework/peer.h>
+#include <services/discovery.h>
+#include <services/discovery_udp.h>
+
+#if SERVICE_Locator
+
+static const char * LOCATOR = "Locator";
+static int peer_cnt = 0;
+
+static int write_peer_properties(PeerServer * ps, void * arg) {
+ int i;
+ OutputStream * out = (OutputStream *)arg;
+
+ if (peer_cnt > 0) write_stream(out, ',');
+ write_stream(out, '{');
+ json_write_string(out, "ID");
+ write_stream(out, ':');
+ json_write_string(out, ps->id);
+ for (i = 0; i < ps->ind; i++) {
+ write_stream(out, ',');
+ json_write_string(out, ps->list[i].name);
+ write_stream(out, ':');
+ json_write_string(out, ps->list[i].value);
+ }
+ write_stream(out, '}');
+ peer_cnt++;
+ return 0;
+}
+
+static void command_sync(char * token, Channel * c) {
+ if (read_stream(&c->inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
+ write_stringz(&c->out, "R");
+ write_stringz(&c->out, token);
+ write_errno(&c->out, 0);
+ write_stream(&c->out, MARKER_EOM);
+}
+
+typedef struct RedirectInfo {
+ Channel * channel;
+ char token[256];
+} RedirectInfo;
+
+static void connect_done(void * args, int error, Channel * c2) {
+ RedirectInfo * info = (RedirectInfo *)args;
+ Channel * c1 = info->channel;
+
+ if (!is_channel_closed(c1)) {
+ assert(c1->state == ChannelStateRedirectReceived);
+ if (!error) {
+ proxy_create(c1, c2);
+ }
+ else {
+ c1->state = ChannelStateConnected;
+ }
+ write_stringz(&c1->out, "R");
+ write_stringz(&c1->out, info->token);
+ write_errno(&c1->out, error);
+ write_stream(&c1->out, MARKER_EOM);
+ }
+ else if (!error) {
+ channel_close(c2);
+ }
+ channel_unlock(c1);
+ loc_free(info);
+}
+
+static void read_peer_attr(InputStream * inp, const char * name, void * x) {
+ peer_server_addprop((PeerServer *)x, loc_strdup(name), json_read_alloc_string(inp));
+}
+
+static void command_redirect(char * token, Channel * c) {
+ PeerServer * ps = NULL;
+ int free_ps = 0;
+
+ assert(c->state == ChannelStateConnected);
+ if (peek_stream(&c->inp) == '{') {
+ ps = peer_server_alloc();
+ json_read_struct(&c->inp, read_peer_attr, ps);
+ free_ps = 1;
+ }
+ else {
+ char id[256];
+ json_read_string(&c->inp, id, sizeof(id));
+ ps = peer_server_find(id);
+ }
+ if (read_stream(&c->inp) != 0) exception(ERR_JSON_SYNTAX);
+ if (read_stream(&c->inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
+
+ if (ps != NULL) {
+ RedirectInfo * info = (RedirectInfo *)loc_alloc_zero(sizeof(RedirectInfo));
+ channel_lock(c);
+ c->state = ChannelStateRedirectReceived;
+ info->channel = c;
+ strlcpy(info->token, token, sizeof(info->token));
+ channel_connect(ps, connect_done, info);
+ }
+ else {
+ write_stringz(&c->out, "R");
+ write_stringz(&c->out, token);
+ write_errno(&c->out, ERR_UNKNOWN_PEER);
+ write_stream(&c->out, MARKER_EOM);
+ }
+ if (free_ps) peer_server_free(ps);
+}
+
+static void command_get_peers(char * token, Channel * c) {
+ if (read_stream(&c->inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
+
+ write_stringz(&c->out, "R");
+ write_stringz(&c->out, token);
+ write_errno(&c->out, 0);
+ write_stream(&c->out, '[');
+ peer_cnt = 0;
+ peer_server_iter(write_peer_properties, &c->out);
+ write_stream(&c->out, ']');
+ write_stream(&c->out, 0);
+ write_stream(&c->out, MARKER_EOM);
+}
+
+static void peer_change_event(PeerServer * ps, int type, void * arg) {
+ OutputStream * out = (OutputStream *)arg;
+
+ if ((ps->flags & PS_FLAG_DISCOVERABLE) == 0) return;
+ write_stringz(out, "E");
+ write_stringz(out, LOCATOR);
+ switch (type) {
+ case PS_EVENT_ADDED:
+ write_stringz(out, "peerAdded");
+ peer_cnt = 0;
+ write_peer_properties(ps, out);
+ break;
+ case PS_EVENT_CHANGED:
+ write_stringz(out, "peerChanged");
+ peer_cnt = 0;
+ write_peer_properties(ps, out);
+ break;
+ case PS_EVENT_HEART_BEAT:
+ write_stringz(out, "peerHeartBeat");
+ json_write_string(out, ps->id);
+ break;
+ case PS_EVENT_REMOVED:
+ write_stringz(out, "peerRemoved");
+ json_write_string(out, ps->id);
+ break;
+ }
+ write_stream(out, 0);
+ write_stream(out, MARKER_EOM);
+}
+
+void ini_locator_service(Protocol * p, TCFBroadcastGroup * bcg) {
+ assert(is_dispatch_thread());
+ peer_server_add_listener(peer_change_event, &bcg->out);
+ add_command_handler(p, LOCATOR, "sync", command_sync);
+ add_command_handler(p, LOCATOR, "redirect", command_redirect);
+ add_command_handler(p, LOCATOR, "getPeers", command_get_peers);
+}
+#endif /* SERVICE_Locator */
+
+void discovery_start(void) {
+#if ENABLE_Discovery
+ discovery_start_udp();
+#endif
+}
+
+void discovery_stop(void) {
+#if ENABLE_Discovery
+ discovery_stop_udp();
+#endif
+}
diff --git a/agent/tcf/services/discovery.h b/agent/tcf/services/discovery.h
new file mode 100644
index 00000000..9788d1dd
--- /dev/null
+++ b/agent/tcf/services/discovery.h
@@ -0,0 +1,50 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+
+/*
+ * Discovery interface
+ */
+
+#ifndef D_discovery
+#define D_discovery
+
+#include <config.h>
+#include <framework/protocol.h>
+#include <framework/channel.h>
+#include <framework/link.h>
+
+#define DISCOVERY_TCF_PORT 1534
+
+/*
+ * Start discovery of remote peers. If no other discovery master exist on the
+ * local machine, then this instance will become master, otherwise a
+ * agent will attempt to connect to the existing master. If the
+ * existing master disappears, then a new attempt will be made to
+ * become master or connect as a client.
+ */
+extern void discovery_start(void);
+
+/*
+ * Stop discovery of remote peers.
+ */
+extern void discovery_stop(void);
+
+#if SERVICE_Locator
+
+extern void ini_locator_service(Protocol * p, TCFBroadcastGroup * bcg);
+
+#endif /* SERVICE_Locator */
+
+#endif /* D_discovery */
diff --git a/agent/tcf/services/discovery_udp.c b/agent/tcf/services/discovery_udp.c
new file mode 100644
index 00000000..8cc86eb9
--- /dev/null
+++ b/agent/tcf/services/discovery_udp.c
@@ -0,0 +1,879 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+
+/*
+ * Implements UDP based service discovery.
+ *
+ * The discovery protocol uses unicast and multicast UDP packets to propagate information
+ * about available TCF peers. The protocol is truly distributed - all participants have
+ * same functionality and no central authority is defined.
+ *
+ * TCF discovery scope is one subnet. Access across subnets is supported by TCF proxy.
+ *
+ * TCF discovery participants use a dedicated UDP port - 1534, however discovery will
+ * work fine if the port is not available for some participants, but at least one
+ * participant on a subnet must be able to bind itself to the default port, otherwise the protocol
+ * will not function properly. An agent that owns a default port is called "master",
+ * an agent that owns non-default port is called "slave".
+ *
+ * Every slave will check periodically availability of default port, and can become a master if
+ * the port becomes available.
+ *
+ * Since slaves cannot receive multicast packets, each agent maintains a list of slaves,
+ * and uses unicast packets to sent info to agents from the list.
+ */
+
+#include <config.h>
+
+#if ENABLE_Discovery
+
+#include <stddef.h>
+#include <errno.h>
+#include <assert.h>
+#include <string.h>
+#include <framework/mdep-inet.h>
+#include <framework/tcf.h>
+#include <framework/myalloc.h>
+#include <framework/events.h>
+#include <framework/errors.h>
+#include <framework/trace.h>
+#include <framework/peer.h>
+#include <framework/ip_ifc.h>
+#include <framework/asyncreq.h>
+#include <services/discovery.h>
+#include <services/discovery_udp.h>
+
+#define MAX_IFC 10
+#define MAX_RECV_ERRORS 8
+
+static int ifc_cnt;
+static ip_ifc_info ifc_list[MAX_IFC];
+static time_t last_req_slaves_time[MAX_IFC];
+static int send_all_ok[MAX_IFC];
+
+static int udp_server_port = 0;
+static int udp_server_socket = -1;
+static int udp_server_generation = 0;
+
+static AsyncReqInfo recvreq;
+static int recvreq_pending = 0;
+static int recvreq_error_cnt = 0;
+static int recvreq_generation = 0;
+static struct sockaddr_in recvreq_addr;
+
+static char recv_buf[MAX_PACKET_SIZE];
+static char send_buf[MAX_PACKET_SIZE];
+static int send_size;
+
+static time_t last_master_packet_time = 0;
+
+static int discovery_stopped = 0;
+
+typedef struct SlaveInfo {
+ struct sockaddr_in addr;
+ time_t last_packet_time; /* Time of last packet from this slave */
+ time_t last_req_slaves_time; /* Time of last UDP_REQ_SLAVES packet from this slave */
+} SlaveInfo;
+
+static SlaveInfo * slave_info = NULL;
+static int slave_cnt = 0;
+static int slave_max = 0;
+
+static void app_char(char ch) {
+ if (send_size < (int)sizeof(send_buf)) {
+ send_buf[send_size++] = ch;
+ }
+}
+
+static void app_str(const char * str) {
+ while (*str && send_size < (int)sizeof(send_buf)) {
+ send_buf[send_size++] = *str++;
+ }
+}
+
+static void app_strz(const char * str) {
+ app_str(str);
+ app_char(0);
+}
+
+static int get_slave_addr(char * buf, ssize_t * pos, struct sockaddr_in * addr, uint64_t * timestamp) {
+ char * port = buf + *pos;
+ char * stmp = buf + *pos;
+ char * host = buf + *pos;
+ size_t len = strlen(buf + *pos);
+ uint64_t ts = 0;
+ int n = 0;
+
+ while (*port && *port != ':') port++;
+ if (*port == ':') *port++ = 0;
+
+ host = port;
+ while (*host && *host != ':') host++;
+ if (*host == ':') *host++ = 0;
+
+ *pos += len + 1;
+
+ memset(addr, 0, sizeof(*addr));
+ addr->sin_family = AF_INET;
+ if (inet_pton(AF_INET, host, &addr->sin_addr) <= 0) return 0;
+ n = atoi(port);
+ if (n == DISCOVERY_TCF_PORT) return 0;
+ addr->sin_port = htons((unsigned short)n);
+ while (*stmp >= '0' && *stmp <= '9') {
+ ts = (ts * 10) + (*stmp++ - '0');
+ }
+ *timestamp = ts;
+ return 1;
+}
+
+static void trigger_recv(void);
+static void udp_server_recv(void * x);
+
+static void delayed_server_recv(void * x) {
+ assert(recvreq_pending);
+ if (recvreq_generation != udp_server_generation) {
+ /* Cancel and restart */
+ recvreq_pending = 0;
+ trigger_recv();
+ }
+ else {
+ async_req_post(&recvreq);
+ }
+}
+
+static void trigger_recv(void) {
+ if (recvreq_pending || udp_server_socket < 0) return;
+ recvreq_pending = 1;
+ recvreq_generation = udp_server_generation;
+ recvreq.done = udp_server_recv;
+ recvreq.client_data = NULL;
+ recvreq.type = AsyncReqRecvFrom;
+ recvreq.u.sio.sock = udp_server_socket;
+ recvreq.u.sio.flags = 0;
+ recvreq.u.sio.bufp = recv_buf;
+ recvreq.u.sio.bufsz = sizeof recv_buf;
+ recvreq.u.sio.addr = (struct sockaddr *)&recvreq_addr;
+ recvreq.u.sio.addrlen = sizeof recvreq_addr;
+ memset(&recvreq_addr, 0, sizeof recvreq_addr);
+ if (recvreq_error_cnt >= MAX_RECV_ERRORS) {
+ /* Delay the request to avoid flooding with error reports */
+ trace(LOG_ALWAYS, "delayed_server_recv error occured: %d", recvreq_error_cnt);
+ post_event_with_delay(delayed_server_recv, NULL, 1000000);
+ }
+ else {
+ async_req_post(&recvreq);
+ }
+}
+
+static int create_server_socket(void) {
+ int sock = -1;
+ int error = 0;
+ const char * reason = NULL;
+ const int i = 1;
+ struct addrinfo hints;
+ struct addrinfo * reslist = NULL;
+ struct addrinfo * res = NULL;
+ struct sockaddr_in local_addr;
+#if defined(_WRS_KERNEL)
+ int local_addr_size = sizeof(local_addr);
+#else
+ socklen_t local_addr_size = sizeof(local_addr);
+#endif
+ char port_str[16];
+
+ sprintf(port_str, "%d", DISCOVERY_TCF_PORT);
+ memset(&local_addr, 0, sizeof(local_addr));
+ memset(&hints, 0, sizeof hints);
+ hints.ai_family = PF_INET;
+ hints.ai_socktype = SOCK_DGRAM;
+ hints.ai_protocol = IPPROTO_UDP;
+ hints.ai_flags = AI_PASSIVE;
+ error = loc_getaddrinfo(NULL, port_str, &hints, &reslist);
+ if (error) {
+ trace(LOG_ALWAYS, "getaddrinfo error: %s", loc_gai_strerror(error));
+ return set_gai_errno(error);
+ }
+ for (res = reslist; res != NULL; res = res->ai_next) {
+ sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
+ if (sock < 0) {
+ error = errno;
+ reason = "create";
+ continue;
+ }
+ if (setsockopt(sock, SOL_SOCKET, SO_BROADCAST, (char *)&i, sizeof(i)) < 0) {
+ error = errno;
+ reason = "setsockopt(SO_BROADCAST)";
+ closesocket(sock);
+ sock = -1;
+ continue;
+ }
+ if (bind(sock, res->ai_addr, res->ai_addrlen)) {
+ error = errno;
+ if (res->ai_addr->sa_family == AF_INET) {
+ struct sockaddr_in addr;
+ trace(LOG_DISCOVERY, "Cannot bind to default UDP port %d: %s",
+ DISCOVERY_TCF_PORT, errno_to_str(error));
+ assert(sizeof(addr) >= res->ai_addrlen);
+ memset(&addr, 0, sizeof(addr));
+ memcpy(&addr, res->ai_addr, res->ai_addrlen);
+ addr.sin_port = 0;
+ error = 0;
+ if (bind(sock, (struct sockaddr *)&addr, sizeof(addr))) {
+ error = errno;
+ if (udp_server_socket >= 0 && recvreq_error_cnt < MAX_RECV_ERRORS) {
+ loc_freeaddrinfo(reslist);
+ closesocket(sock);
+ return 0;
+ }
+ }
+ }
+ if (error) {
+ reason = "bind";
+ closesocket(sock);
+ sock = -1;
+ continue;
+ }
+ }
+ if (getsockname(sock, (struct sockaddr *)&local_addr, &local_addr_size)) {
+ error = errno;
+ reason = "getsockname";
+ closesocket(sock);
+ sock = -1;
+ continue;
+ }
+ /* Only bind once - don't see how getaddrinfo with the given
+ * arguments could return more then one anyway */
+ break;
+ }
+ if (sock < 0) {
+ assert(error);
+ loc_freeaddrinfo(reslist);
+ if (udp_server_socket >= 0 && recvreq_error_cnt < MAX_RECV_ERRORS) {
+ return 0;
+ }
+ trace(LOG_ALWAYS, "Discovery service socket %s error: %s",
+ reason, errno_to_str(error));
+ return error;
+ }
+
+ if (udp_server_socket >= 0) closesocket(udp_server_socket);
+ udp_server_port = ntohs(local_addr.sin_port);
+ udp_server_socket = sock;
+ udp_server_generation++;
+ loc_freeaddrinfo(reslist);
+ trace(LOG_DISCOVERY, "UDP discovery server created at port %d", udp_server_port);
+ trigger_recv();
+ return 0;
+}
+
+static int send_packet(ip_ifc_info * ifc, struct sockaddr_in * addr) {
+ if (addr == NULL) {
+ /* Broadcast */
+ int n = 0;
+ static struct sockaddr_in buf;
+
+ /* Send to all slaves */
+ while (n < slave_cnt) {
+ SlaveInfo * s = slave_info + n++;
+ send_packet(ifc, &s->addr);
+ }
+
+ /* Send to all masters by using interface broadcast address */
+ memset(&buf, 0, sizeof(buf));
+ addr = &buf;
+ addr->sin_family = AF_INET;
+ addr->sin_port = htons(DISCOVERY_TCF_PORT);
+ addr->sin_addr.s_addr = ifc->addr;
+ if (*(uint8_t *)&ifc->addr != 127) addr->sin_addr.s_addr |= ~ifc->mask;
+ }
+
+ /* Don't send if address does not belong to subnet of the interface */
+ if ((ifc->addr & ifc->mask) != (addr->sin_addr.s_addr & ifc->mask)) return 0;
+
+ /* Don't send to ourselves */
+ if (ifc->addr == addr->sin_addr.s_addr && udp_server_port == ntohs(addr->sin_port)) return 0;
+
+#if ENABLE_Trace
+ if (log_file != NULL && (log_mode & LOG_DISCOVERY) != 0) {
+ int i;
+ char buf[sizeof(send_buf) + 32];
+ size_t pos = 0;
+ char ch;
+ switch (send_buf[4]) {
+ case UDP_ACK_INFO:
+ pos = strlcpy(buf, "ACK_INFO", sizeof(buf));
+ i = 8;
+ while (i < send_size) {
+ if (strncmp(send_buf + i, "ID=", 3) == 0) {
+ if (pos < sizeof(buf) - 1) buf[pos++] = ' ';
+ while (i < send_size && (ch = send_buf[i++]) != 0) {
+ if (pos < sizeof(buf) - 1) buf[pos++] = ch;
+ }
+ break;
+ }
+ else {
+ while (i < send_size && send_buf[i++]) {}
+ }
+ }
+ break;
+ case UDP_ACK_SLAVES:
+ pos = strlcpy(buf, "ACK_SLAVES", sizeof(buf));
+ i = 8;
+ while (i < send_size) {
+ if (pos < sizeof(buf) - 1) buf[pos++] = ' ';
+ while (i < send_size && (ch = send_buf[i++]) != 0) {
+ if (pos < sizeof(buf) - 1) buf[pos++] = ch;
+ }
+ }
+ break;
+ case UDP_REQ_INFO:
+ pos = strlcpy(buf, "REQ_INFO", sizeof(buf));
+ break;
+ case UDP_REQ_SLAVES:
+ pos = strlcpy(buf, "REQ_SLAVES", sizeof(buf));
+ break;
+ case UDP_PEERS_REMOVED:
+ pos = strlcpy(buf, "PEERS_REMOVED", sizeof(buf));
+ i = 8;
+ while (i < send_size) {
+ if (pos < sizeof(buf) - 1) buf[pos++] = ' ';
+ while (i < send_size && (ch = send_buf[i++]) != 0) {
+ if (pos < sizeof(buf) - 1) buf[pos++] = ch;
+ }
+ }
+ break;
+ default:
+ pos = strlcpy(buf, "???", sizeof(buf));
+ break;
+ }
+ buf[pos] = 0;
+ trace(LOG_DISCOVERY, "%s to %s:%d", buf, inet_ntoa(addr->sin_addr), ntohs(addr->sin_port));
+ }
+#endif
+
+ if (sendto(udp_server_socket, send_buf, send_size, 0, (struct sockaddr *)addr, sizeof(*addr)) >= 0) return 1;
+
+ trace(LOG_ALWAYS, "Can't send UDP discovery packet to %s:%d %s",
+ inet_ntoa(addr->sin_addr), ntohs(addr->sin_port), errno_to_str(errno));
+ return 0;
+}
+
+static int udp_send_peer_info(PeerServer * ps, void * arg) {
+ struct sockaddr_in * addr = (struct sockaddr_in *)arg;
+ const char * host = NULL;
+ const char * prot = NULL;
+ struct in_addr peer_addr;
+ int n;
+
+ if ((ps->flags & PS_FLAG_PRIVATE) != 0) return 0;
+ if ((ps->flags & PS_FLAG_DISCOVERABLE) == 0) return 0;
+
+ memset(&peer_addr, 0, sizeof(peer_addr));
+ prot = peer_server_getprop(ps, "TransportName", NULL);
+ if (prot != NULL && (strcmp(prot, "TCP") == 0 || strcmp(prot, "SSL") == 0)) {
+ host = peer_server_getprop(ps, "Host", NULL);
+ if (host == NULL || inet_pton(AF_INET, host, &peer_addr) <= 0) return 0;
+ }
+
+ send_size = 8;
+
+ for (n = 0; n < ifc_cnt; n++) {
+ ip_ifc_info * ifc = ifc_list + n;
+
+ if ((ps->flags & PS_FLAG_LOCAL) == 0) {
+ /* Info about non-local peers is sent only by master */
+ if (udp_server_port != DISCOVERY_TCF_PORT) return 0;
+ if (host == NULL) return 0;
+ if (ifc->addr != htonl(INADDR_LOOPBACK) && ifc->addr != peer_addr.s_addr) continue;
+ }
+
+ if (ifc->addr != htonl(INADDR_LOOPBACK)) {
+ if (host == NULL) continue;
+ assert(peer_addr.s_addr != INADDR_ANY);
+ if ((ifc->addr & ifc->mask) != (peer_addr.s_addr & ifc->mask)) {
+ /* Peer address does not belong to subnet of this interface */
+ continue;
+ }
+ }
+
+ if (send_size == 8) {
+ int i;
+ send_buf[4] = UDP_ACK_INFO;
+ app_str("ID=");
+ app_strz(ps->id);
+ for (i = 0; i < ps->ind; i++) {
+ const char * name = ps->list[i].name;
+ assert(strcmp(name, "ID") != 0);
+ app_str(name);
+ app_char('=');
+ app_strz(ps->list[i].value);
+ }
+ }
+
+ send_all_ok[n] = 1;
+ send_packet(ifc, addr);
+ }
+ return 0;
+}
+
+static void udp_send_ack_info(struct sockaddr_in * addr) {
+ assert(is_dispatch_thread());
+ peer_server_iter(udp_send_peer_info, addr);
+}
+
+static void udp_send_req_info(struct sockaddr_in * addr) {
+ int n;
+ for (n = 0; n < ifc_cnt; n++) {
+ ip_ifc_info * ifc = ifc_list + n;
+
+ send_size = 8;
+ send_buf[4] = UDP_REQ_INFO;
+ send_packet(ifc, addr);
+ }
+}
+
+static void udp_send_empty_packet(struct sockaddr_in * addr) {
+ int n;
+ for (n = 0; n < ifc_cnt; n++) {
+ ip_ifc_info * ifc = ifc_list + n;
+
+ if (send_all_ok[n]) continue;
+
+ send_size = 8;
+ send_buf[4] = UDP_ACK_SLAVES;
+ send_packet(ifc, addr);
+ }
+}
+
+static void udp_send_req_slaves(ip_ifc_info * ifc, struct sockaddr_in * addr) {
+ send_size = 8;
+ send_buf[4] = UDP_REQ_SLAVES;
+ send_packet(ifc, addr);
+}
+
+static void udp_send_ack_slaves_one(SlaveInfo * s) {
+ ip_ifc_info * ifc;
+ time_t timenow = time(NULL);
+ int ttl = (int)(s->last_packet_time + PEER_DATA_RETENTION_PERIOD - timenow) * 1000;
+
+ if (ttl <= 0) return;
+
+ for (ifc = ifc_list; ifc < &ifc_list[ifc_cnt]; ifc++) {
+ int n = 0;
+ char str[256];
+ if ((ifc->addr & ifc->mask) != (s->addr.sin_addr.s_addr & ifc->mask)) continue;
+
+ send_size = 8;
+ send_buf[4] = UDP_ACK_SLAVES;
+ snprintf(str, sizeof(str), "%u:%u:%s", ttl, ntohs(s->addr.sin_port), inet_ntoa(s->addr.sin_addr));
+ app_strz(str);
+
+ while (n < slave_cnt) {
+ SlaveInfo * s = slave_info + n++;
+ if (s->last_req_slaves_time + PEER_DATA_RETENTION_PERIOD < timenow) continue;
+ send_packet(ifc, &s->addr);
+ }
+ }
+}
+
+static void udp_send_ack_slaves_all(struct sockaddr_in * addr, time_t timenow) {
+ int k;
+
+ for (k = 0; k < ifc_cnt; k++) {
+ int n = 0;
+ ip_ifc_info * ifc = ifc_list + k;
+
+ if ((ifc->addr & ifc->mask) != (addr->sin_addr.s_addr & ifc->mask)) continue;
+
+ send_size = 8;
+ send_buf[4] = UDP_ACK_SLAVES;
+
+ while (n < slave_cnt) {
+ char str[256];
+ SlaveInfo * s = slave_info + n++;
+ int ttl = (int)(s->last_packet_time + PEER_DATA_RETENTION_PERIOD - timenow) * 1000;
+ if (ttl <= 0) continue;
+ if (addr->sin_addr.s_addr == s->addr.sin_addr.s_addr && addr->sin_port == s->addr.sin_port) continue;
+ if (ifc->addr != htonl(INADDR_LOOPBACK)) {
+ if ((ifc->addr & ifc->mask) != (s->addr.sin_addr.s_addr & ifc->mask)) {
+ /* Slave address does not belong to subnet of this interface */
+ continue;
+ }
+ }
+ snprintf(str, sizeof(str), "%u:%u:%s", ttl, ntohs(s->addr.sin_port), inet_ntoa(s->addr.sin_addr));
+ if (send_size + strlen(str) >= PREF_PACKET_SIZE) {
+ send_packet(ifc, addr);
+ send_size = 8;
+ }
+ app_strz(str);
+ send_all_ok[k] = 1;
+ }
+
+ if (send_size > 8) send_packet(ifc, addr);
+ }
+}
+
+static int add_peer_id(PeerServer * ps, void * arg) {
+ if ((ps->flags & PS_FLAG_PRIVATE) != 0) return 0;
+ if ((ps->flags & PS_FLAG_DISCOVERABLE) == 0) return 0;
+ if ((ps->flags & PS_FLAG_LOCAL) == 0) return 0;
+ app_strz(ps->id);
+ return 0;
+}
+
+static void udp_send_peer_removed(void) {
+ int n;
+ send_size = 8;
+ send_buf[4] = UDP_PEERS_REMOVED;
+ peer_server_iter(add_peer_id, NULL);
+ for (n = 0; n < ifc_cnt; n++) {
+ ip_ifc_info * ifc = ifc_list + n;
+ send_packet(ifc, NULL);
+ }
+}
+
+static void udp_send_all(struct sockaddr_in * addr, SlaveInfo * s) {
+ memset(send_all_ok, 0, sizeof(send_all_ok));
+ udp_send_ack_info(addr);
+ if (addr != NULL && s != NULL) {
+ time_t timenow = time(NULL);
+ if (s->last_req_slaves_time + PEER_DATA_RETENTION_PERIOD >= timenow) {
+ udp_send_ack_slaves_all(addr, timenow);
+ }
+ }
+ udp_send_empty_packet(addr);
+}
+
+static SlaveInfo * add_slave(struct sockaddr_in * addr, time_t timestamp) {
+ int i = 0;
+ SlaveInfo * s = NULL;
+ while (i < slave_cnt) {
+ s = slave_info + i++;
+ if (memcmp(&s->addr, addr, sizeof(struct sockaddr_in)) == 0) {
+ if (s->last_packet_time < timestamp) s->last_packet_time = timestamp;
+ return s;
+ }
+ }
+ if (slave_max == 0) {
+ assert(slave_cnt == 0);
+ slave_max = 16;
+ slave_info = (SlaveInfo *)loc_alloc(sizeof(SlaveInfo) * slave_max);
+ }
+ else if (slave_cnt >= slave_max) {
+ assert(slave_cnt == slave_max);
+ slave_max *= 2;
+ slave_info = (SlaveInfo *)loc_realloc(slave_info, sizeof(SlaveInfo) * slave_max);
+ }
+ s = slave_info + slave_cnt++;
+ s->addr = *addr;
+ s->last_packet_time = timestamp;
+ s->last_req_slaves_time = 0;
+ udp_send_req_info(addr);
+ udp_send_all(addr, s);
+ udp_send_ack_slaves_one(s);
+ return s;
+}
+
+static void udp_refresh_timer(void * arg) {
+ time_t timenow = time(NULL);
+
+ if (discovery_stopped) return;
+
+ if (slave_cnt > 0) {
+ /* Cleanup slave table */
+ int i = 0;
+ int j = 0;
+ while (i < slave_cnt) {
+ SlaveInfo * s = slave_info + i++;
+ if (s->last_packet_time + PEER_DATA_RETENTION_PERIOD >= timenow) {
+ if (j < i) slave_info[j] = *s;
+ j++;
+ }
+ }
+ slave_cnt = j;
+ }
+
+ if (udp_server_port != DISCOVERY_TCF_PORT && last_master_packet_time + PEER_DATA_RETENTION_PERIOD / 2 <= timenow) {
+ /* No master reponces, try to become a master */
+ create_server_socket();
+ }
+
+ /* Refresh list of network interfaces */
+ ifc_cnt = build_ifclist(udp_server_socket, MAX_IFC, ifc_list);
+
+ if (udp_server_port != DISCOVERY_TCF_PORT) {
+ int i;
+ for (i = 0; i < ifc_cnt; i++) {
+ struct sockaddr_in addr;
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons((short)udp_server_port);
+ addr.sin_addr.s_addr = ifc_list[i].addr;
+ add_slave(&addr, timenow);
+ }
+ }
+
+ /* Broadcast peer info */
+ udp_send_all(NULL, NULL);
+
+ post_event_with_delay(udp_refresh_timer, NULL, PEER_DATA_REFRESH_PERIOD * 1000000);
+}
+
+static int is_remote(struct sockaddr_in * addr) {
+ int i;
+
+ if (ntohs(addr->sin_port) != udp_server_port) return 1;
+ for (i = 0; i < ifc_cnt; i++) {
+ if (addr->sin_addr.s_addr == ifc_list[i].addr) return 0;
+ }
+ return 1;
+}
+
+static void udp_receive_req_info(SlaveInfo * s) {
+ trace(LOG_DISCOVERY, "REQ_INFO from %s:%d",
+ inet_ntoa(recvreq_addr.sin_addr), ntohs(recvreq_addr.sin_port));
+ udp_send_all(&recvreq_addr, s);
+}
+
+static void udp_receive_ack_info(void) {
+ PeerServer * ps = peer_server_alloc();
+ char * p = recv_buf + 8;
+ char * e = recv_buf + recvreq.u.sio.rval;
+
+ assert(is_dispatch_thread());
+ while (p < e) {
+ char * name = p;
+ char * value = NULL;
+ while (p < e && *p != '\0' && *p != '=') p++;
+ if (p >= e || *p != '=') {
+ p = NULL;
+ break;
+ }
+ *p++ = '\0';
+ value = p;
+ while (p < e && *p != '\0') p++;
+ if (p >= e) {
+ p = NULL;
+ break;
+ }
+ peer_server_addprop(ps, loc_strdup(name), loc_strdup(value));
+ p++;
+ }
+ if (p != NULL && ps->id != NULL) {
+ const char * prot = peer_server_getprop(ps, "TransportName", NULL);
+ if (prot != NULL && (strcmp(prot, "TCP") == 0 || strcmp(prot, "SSL") == 0)) {
+ const char * host = peer_server_getprop(ps, "Host", NULL);
+ if (host != NULL) {
+ struct in_addr peer_addr;
+ memset(&peer_addr, 0, sizeof(peer_addr));
+ if (inet_pton(AF_INET, host, &peer_addr) > 0) {
+ int n;
+ for (n = 0; n < ifc_cnt; n++) {
+ ip_ifc_info * ifc = ifc_list + n;
+ if ((ifc->addr & ifc->mask) == (peer_addr.s_addr & ifc->mask)) {
+ trace(LOG_DISCOVERY, "ACK_INFO from %s:%d, ID=%s",
+ inet_ntoa(recvreq_addr.sin_addr), ntohs(recvreq_addr.sin_port), ps->id);
+ ps->flags = PS_FLAG_DISCOVERABLE;
+ peer_server_add(ps, PEER_DATA_RETENTION_PERIOD);
+ return;
+ }
+ }
+ }
+ }
+ }
+ }
+ trace(LOG_DISCOVERY, "Received malformed ACK_INFO from %s:%d",
+ inet_ntoa(recvreq_addr.sin_addr), ntohs(recvreq_addr.sin_port));
+ peer_server_free(ps);
+}
+
+static void udp_receive_req_slaves(SlaveInfo * s, time_t timenow) {
+ trace(LOG_DISCOVERY, "REQ_SLAVES from %s:%d",
+ inet_ntoa(recvreq_addr.sin_addr), ntohs(recvreq_addr.sin_port));
+ if (s != NULL) s->last_req_slaves_time = timenow;
+ udp_send_ack_slaves_all(&recvreq_addr, timenow);
+}
+
+static void udp_receive_ack_slaves(time_t timenow) {
+ ssize_t pos = 8;
+ ssize_t len = recvreq.u.sio.rval;
+ while (pos < len) {
+ struct sockaddr_in addr;
+ uint64_t timestamp;
+ if (get_slave_addr(recv_buf, &pos, &addr, &timestamp)) {
+ time_t delta = 60 * 10; /* 10 minutes */
+ time_t timeval = 0;
+ if (timestamp < 3600000) {
+ /* Timestamp is "time to live" in milliseconds */
+ timeval = timenow + (time_t)(timestamp / 1000) - PEER_DATA_RETENTION_PERIOD;
+ }
+ else if (timestamp < (uint64_t)timenow + 50000000) {
+ /* Timestamp is in seconds */
+ timeval = (time_t)timestamp;
+ }
+ else {
+ /* Timestamp is in milliseconds */
+ timeval = (time_t)(timestamp / 1000);
+ }
+ if (log_mode & LOG_DISCOVERY) {
+ char buf[64];
+ snprintf(buf, sizeof(buf), "%s:%d", inet_ntoa(recvreq_addr.sin_addr), ntohs(recvreq_addr.sin_port));
+ trace(LOG_DISCOVERY, "ACK_SLAVES %"PRId64":%u:%s from %s",
+ timestamp, ntohs(addr.sin_port), inet_ntoa(addr.sin_addr), buf);
+ }
+ if (timeval < timenow - delta || timeval > timenow + delta) {
+ trace(LOG_DISCOVERY, "Discovery: invalid slave info timestamp %"PRId64" from %s:%d",
+ timestamp, inet_ntoa(recvreq_addr.sin_addr), ntohs(recvreq_addr.sin_port));
+ timeval = timenow - PEER_DATA_RETENTION_PERIOD / 2;
+ }
+ add_slave(&addr, timeval);
+ }
+ }
+}
+
+static void udp_receive_peer_removed(void) {
+ char * p = recv_buf + 8;
+ char * e = recv_buf + recvreq.u.sio.rval;
+
+ assert(is_dispatch_thread());
+ while (p < e) {
+ char * id = p;
+ while (p < e && *p != '\0') p++;
+ if (p < e) {
+ PeerServer * peer = peer_server_find(id);
+ if (peer != NULL && (peer->flags & PS_FLAG_LOCAL) == 0) {
+ peer_server_remove(id);
+ }
+ while (p < e && *p == '\0') p++;
+ }
+ }
+}
+
+static void udp_server_recv(void * x) {
+ assert(recvreq_pending != 0);
+ assert(x == &recvreq);
+ if (discovery_stopped) {
+ if (udp_server_socket >= 0) {
+ closesocket(udp_server_socket);
+ udp_server_socket = -1;
+ }
+ return;
+ }
+ recvreq_pending = 0;
+ if (recvreq.error != 0) {
+ if (recvreq_generation != udp_server_generation) {
+ recvreq_error_cnt = 0;
+ }
+ else {
+ recvreq_error_cnt++;
+ trace(LOG_ALWAYS, "UDP socket receive failed: %s", errno_to_str(recvreq.error));
+ }
+ }
+ else {
+ recvreq_error_cnt = 0;
+ if (recvreq.u.sio.rval >= 8 &&
+ recv_buf[0] == 'T' &&
+ recv_buf[1] == 'C' &&
+ recv_buf[2] == 'F' &&
+ recv_buf[3] == UDP_VERSION &&
+ is_remote(&recvreq_addr)) {
+ if (recv_buf[4] == UDP_PEERS_REMOVED) {
+ udp_receive_peer_removed();
+ }
+ else {
+ int n = 0;
+ time_t timenow = time(NULL);
+ SlaveInfo * s = NULL;
+ if (ntohs(recvreq_addr.sin_port) != DISCOVERY_TCF_PORT) {
+ /* Packet from a slave, save its address */
+ s = add_slave(&recvreq_addr, timenow);
+ }
+ switch (recv_buf[4]) {
+ case UDP_REQ_INFO:
+ udp_receive_req_info(s);
+ break;
+ case UDP_ACK_INFO:
+ udp_receive_ack_info();
+ break;
+ case UDP_REQ_SLAVES:
+ udp_receive_req_slaves(s, timenow);
+ break;
+ case UDP_ACK_SLAVES:
+ udp_receive_ack_slaves(timenow);
+ break;
+ }
+ for (n = 0; n < ifc_cnt; n++) {
+ ip_ifc_info * ifc = ifc_list + n;
+ if ((ifc->addr & ifc->mask) == (recvreq_addr.sin_addr.s_addr & ifc->mask)) {
+ time_t delay = PEER_DATA_RETENTION_PERIOD / 3;
+ if (ntohs(recvreq_addr.sin_port) != DISCOVERY_TCF_PORT) delay = PEER_DATA_RETENTION_PERIOD / 3 * 2;
+ else if (recvreq_addr.sin_addr.s_addr != ifc->addr) delay = PEER_DATA_RETENTION_PERIOD / 2;
+ if (last_req_slaves_time[n] + delay <= timenow) {
+ udp_send_req_slaves(ifc, &recvreq_addr);
+ last_req_slaves_time[n] = timenow;
+ }
+ /* Remember time only if local host master */
+ if (ifc->addr == recvreq_addr.sin_addr.s_addr && ntohs(recvreq_addr.sin_port) == DISCOVERY_TCF_PORT) {
+ last_master_packet_time = timenow;
+ }
+ }
+ }
+ }
+ }
+ }
+ trigger_recv();
+}
+
+static void local_peer_changed(PeerServer * ps, int type, void * arg) {
+ trace(LOG_DISCOVERY, "Peer changed: ps=0x%x, type=%d", ps, type);
+ switch (type) {
+ case PS_EVENT_ADDED:
+ case PS_EVENT_CHANGED:
+ udp_send_peer_info(ps, NULL);
+ break;
+ }
+}
+
+int discovery_start_udp(void) {
+ int error = 0;
+ assert(!discovery_stopped);
+ error = create_server_socket();
+ if (error) return error;
+ peer_server_add_listener(local_peer_changed, NULL);
+ post_event_with_delay(udp_refresh_timer, NULL, PEER_DATA_REFRESH_PERIOD * 1000000);
+ ifc_cnt = build_ifclist(udp_server_socket, MAX_IFC, ifc_list);
+ memset(send_buf, 0, sizeof(send_buf));
+ send_buf[0] = 'T';
+ send_buf[1] = 'C';
+ send_buf[2] = 'F';
+ send_buf[3] = UDP_VERSION;
+ udp_send_req_info(NULL);
+ udp_send_all(NULL, NULL);
+ return 0;
+}
+
+int discovery_stop_udp(void) {
+ assert(!discovery_stopped);
+ udp_send_peer_removed();
+ discovery_stopped = 1;
+ if (slave_info != NULL) {
+ loc_free(slave_info);
+ slave_cnt = 0;
+ slave_max = 0;
+ }
+ return 0;
+}
+
+#endif /* ENABLE_Discovery */
diff --git a/agent/tcf/services/discovery_udp.h b/agent/tcf/services/discovery_udp.h
new file mode 100644
index 00000000..fcdb3ec2
--- /dev/null
+++ b/agent/tcf/services/discovery_udp.h
@@ -0,0 +1,52 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+
+/*
+ * Simple UDP based discovery server interface
+ */
+
+#ifndef D_discovery_udp
+#define D_discovery_udp
+
+/*
+ * Packet max payload size, assuming:
+ * IPv6 header: 40 bytes,
+ * UDP header: 8 bytes,
+ * max jumbo packet size: 9000 bytes,
+ * max non-fragmented packet size: 1500 bytes
+ */
+#define MAX_PACKET_SIZE (9000 - 40 - 8)
+#define PREF_PACKET_SIZE (1500 - 40 - 8)
+
+/* UDP discovery packet types: */
+#define UDP_REQ_INFO 1 /* Peer info request */
+#define UDP_ACK_INFO 2 /* Peer info - contains list of peer attributes */
+#define UDP_REQ_SLAVES 3 /* List of slaves request */
+#define UDP_ACK_SLAVES 4 /* List of slaves - contains list of socket addresses */
+#define UDP_PEERS_REMOVED 5 /* List of peers that are removed */
+
+#define UDP_VERSION '2'
+
+/*
+ * Start UDP discovery server
+ */
+extern int discovery_start_udp(void);
+
+/*
+ * Stop UDP discovery server
+ */
+extern int discovery_stop_udp(void);
+
+#endif /* D_discovery_udp */
diff --git a/agent/tcf/services/dwarf.h b/agent/tcf/services/dwarf.h
new file mode 100644
index 00000000..0b6873c4
--- /dev/null
+++ b/agent/tcf/services/dwarf.h
@@ -0,0 +1,574 @@
+/*******************************************************************************
+ * Copyright (c) 1996, 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
+ *******************************************************************************/
+
+/*
+ * DWARF Debugging Information Format.
+ */
+
+#define TAG_padding 0x0000
+#define TAG_array_type 0x0001
+#define TAG_class_type 0x0002
+#define TAG_entry_point 0x0003
+#define TAG_enumeration_type 0x0004
+#define TAG_formal_parameter 0x0005
+#define TAG_global_subroutine 0x0006
+#define TAG_global_variable 0x0007
+#define TAG_imported_declaration 0x0008
+#define TAG_label 0x000a
+#define TAG_lexical_block 0x000b
+#define TAG_local_variable 0x000c
+#define TAG_member 0x000d
+#define TAG_pointer_type 0x000f
+#define TAG_reference_type 0x0010
+#define TAG_compile_unit 0x0011
+#define TAG_source_file 0x0011
+#define TAG_string_type 0x0012
+#define TAG_structure_type 0x0013
+#define TAG_subroutine 0x0014
+#define TAG_subroutine_type 0x0015
+#define TAG_typedef 0x0016
+#define TAG_union_type 0x0017
+#define TAG_unspecified_parameters 0x0018
+#define TAG_variant 0x0019
+#define TAG_common_block 0x001a
+#define TAG_common_inclusion 0x001b
+#define TAG_inheritance 0x001c
+#define TAG_inlined_subroutine 0x001d
+#define TAG_module 0x001e
+#define TAG_ptr_to_member_type 0x001f
+#define TAG_set_type 0x0020
+#define TAG_subrange_type 0x0021
+#define TAG_with_stmt 0x0022
+#define TAG_access_declaration 0x0023
+#define TAG_base_type 0x0024
+#define TAG_catch_block 0x0025
+#define TAG_const_type 0x0026
+#define TAG_constant 0x0027
+#define TAG_enumerator 0x0028
+#define TAG_file_type 0x0029
+#define TAG_friend 0x002a
+#define TAG_namelist 0x002b
+#define TAG_namelist_item 0x002c
+#define TAG_packed_type 0x002d
+#define TAG_subprogram 0x002e
+#define TAG_template_type_param 0x002f
+#define TAG_template_value_param 0x0030
+#define TAG_thrown_type 0x0031
+#define TAG_try_block 0x0032
+#define TAG_variant_part 0x0033
+#define TAG_variable 0x0034
+#define TAG_volatile_type 0x0035
+#define TAG_dwarf_procedure 0x0036
+#define TAG_restrict_type 0x0037
+#define TAG_interface_type 0x0038
+#define TAG_namespace 0x0039
+#define TAG_imported_module 0x003a
+#define TAG_unspecified_type 0x003b
+#define TAG_partial_unit 0x003c
+#define TAG_imported_unit 0x003d
+#define TAG_mutable_type 0x003e
+#define TAG_condition 0x003f
+#define TAG_shared_type 0x0040
+#define TAG_lo_user 0x4080
+#define TAG_wrs_thrown_object 0x4080
+#define TAG_wrs_throw_breakpoint 0x4081
+#define TAG_wrs_catch_breakpoint 0x4082
+#define TAG_wrs_extern_subroutine 0x4083
+#define TAG_hi_user 0xffff
+
+#define CHILDREN_no 0x00
+#define CHILDREN_yes 0x01
+
+#define FORM_ADDR 0x0001
+#define FORM_REF 0x0002
+#define FORM_BLOCK2 0x0003
+#define FORM_BLOCK4 0x0004
+#define FORM_DATA2 0x0005
+#define FORM_DATA4 0x0006
+#define FORM_DATA8 0x0007
+#define FORM_STRING 0x0008
+#define FORM_BLOCK 0x0009
+#define FORM_BLOCK1 0x000a
+#define FORM_DATA1 0x000b
+#define FORM_FLAG 0x000c
+#define FORM_SDATA 0x000d
+#define FORM_STRP 0x000e
+#define FORM_UDATA 0x000f
+#define FORM_REF_ADDR 0x0010
+#define FORM_REF1 0x0011
+#define FORM_REF2 0x0012
+#define FORM_REF4 0x0013
+#define FORM_REF8 0x0014
+#define FORM_REF_UDATA 0x0015
+#define FORM_INDIRECT 0x0016
+
+#define AT_sibling 0x0001
+#define AT_location 0x0002
+#define AT_name 0x0003
+#define AT_fund_type 0x0005
+#define AT_mod_fund_type 0x0006
+#define AT_user_def_type 0x0007
+#define AT_mod_u_d_type 0x0008
+#define AT_ordering 0x0009
+#define AT_subscr_data 0x000a
+#define AT_byte_size 0x000b
+#define AT_bit_offset 0x000c
+#define AT_bit_size 0x000d
+#define AT_element_list 0x000f
+#define AT_stmt_list 0x0010
+#define AT_low_pc 0x0011
+#define AT_high_pc 0x0012
+#define AT_language 0x0013
+#define AT_member 0x0014
+#define AT_discr 0x0015
+#define AT_discr_value 0x0016
+#define AT_visibility 0x0017
+#define AT_import 0x0018
+#define AT_string_length 0x0019
+#define AT_common_reference 0x001a
+#define AT_comp_dir 0x001b
+#define AT_const_value 0x001c
+#define AT_constaining_type 0x001d
+#define AT_default_value 0x001e
+#define AT_friends 0x001f
+#define AT_inline 0x0020
+#define AT_is_optional 0x0021
+#define AT_lower_bound 0x0022
+#define AT_program 0x0023
+#define AT_private 0x0024
+#define AT_producer 0x0025
+#define AT_protected 0x0026
+#define AT_prototyped 0x0027
+#define AT_public 0x0028
+#define AT_pure_virtual 0x0029
+#define AT_return_addr 0x002a
+#define AT_specification_v1 0x002b
+#define AT_start_scope 0x002c
+#define AT_stride_size 0x002e
+#define AT_upper_bound 0x002f
+#define AT_virtual 0x0030
+#define AT_abstract_origin 0x0031
+#define AT_accessibility 0x0032
+#define AT_address_class 0x0033
+#define AT_artificial 0x0034
+#define AT_base_types 0x0035
+#define AT_calling_convention 0x0036
+#define AT_count 0x0037
+#define AT_data_member_location 0x0038
+#define AT_decl_column 0x0039
+#define AT_decl_file 0x003a
+#define AT_decl_line 0x003b
+#define AT_declaration 0x003c
+#define AT_distr_list 0x003d
+#define AT_encoding 0x003e
+#define AT_external 0x003f
+#define AT_frame_base 0x0040
+#define AT_friend 0x0041
+#define AT_identifier_case 0x0042
+#define AT_macro_info 0x0043
+#define AT_namelist_info 0x0044 /* typo? item */
+#define AT_priority 0x0045
+#define AT_segment 0x0046
+#define AT_specification_v2 0x0047 /* v2 */
+#define AT_static_link 0x0048
+#define AT_type 0x0049
+#define AT_use_location 0x004a
+#define AT_variable_parameter 0x004b
+#define AT_virtuality 0x004c
+#define AT_vtable_elem_location 0x004d
+#define AT_allocated 0x004e /* v3 */
+#define AT_associated 0x004f /* v3 */
+#define AT_mangled 0x0050 /* v1 */
+#define AT_data_location 0x0050 /* v2 */
+#define AT_stride 0x0051 /* v3 */
+#define AT_entry_pc 0x0052 /* v3 */
+#define AT_use_UTF8 0x0053 /* v3 */
+#define AT_extension 0x0054 /* v3 */
+#define AT_ranges 0x0055 /* v3 */
+#define AT_trampoline 0x0056 /* v3 */
+#define AT_call_column 0x0057 /* v3 */
+#define AT_call_file 0x0058 /* v3 */
+#define AT_call_line 0x0059 /* v3 */
+#define AT_description 0x005a /* v3 */
+#define AT_endianity 0x0065 /* v3 */
+#define AT_lo_user_v1 0x0200
+#define AT_hi_user_v1 0x03ff
+#define AT_push_mask 0x0220
+#define AT_frame_size 0x0221
+#define AT_main_unit 0x0222
+#define AT_stack_use 0x0223
+#define AT_source_file_names 0x0800
+#define AT_source_info 0x0810
+#define AT_lo_user_v2 0x2000
+#define AT_wrs_options 0x2001
+#define AT_MIPS_linkage_name 0x2007
+#define AT_hi_user_v2 0x3fff
+
+
+#define OP_reg 0x01 /* v1 */
+#define OP_basereg 0x02 /* v1 */
+#define OP_addr 0x03
+#define OP_const 0x04 /* v1 */
+#define OP_deref2 0x05 /* v1 */
+#define OP_deref 0x06
+#define OP_add 0x07 /* v1 */
+#define OP_const1u 0x08
+#define OP_const1s 0x09
+#define OP_const2u 0x0a
+#define OP_const2s 0x0b
+#define OP_const4u 0x0c
+#define OP_const4s 0x0d
+#define OP_const8u 0x0e
+#define OP_const8s 0x0f
+#define OP_constu 0x10
+#define OP_consts 0x11
+#define OP_dup 0x12
+#define OP_drop 0x13
+#define OP_over 0x14
+#define OP_pick 0x15
+#define OP_swap 0x16
+#define OP_rot 0x17
+#define OP_xderef 0x18
+#define OP_abs 0x19
+#define OP_and 0x1a
+#define OP_div 0x1b
+#define OP_minus 0x1c
+#define OP_mod 0x1d
+#define OP_mul 0x1e
+#define OP_neg 0x1f
+#define OP_not 0x20
+#define OP_or 0x21
+#define OP_plus 0x22
+#define OP_plus_uconst 0x23
+#define OP_shl 0x24
+#define OP_shr 0x25
+#define OP_shra 0x26
+#define OP_xor 0x27
+#define OP_bra 0x28
+#define OP_eq 0x29
+#define OP_ge 0x2a
+#define OP_gt 0x2b
+#define OP_le 0x2c
+#define OP_lt 0x2d
+#define OP_ne 0x2e
+#define OP_skip 0x2f
+#define OP_lit0 0x30
+#define OP_lit1 0x31
+#define OP_lit2 0x32
+#define OP_lit3 0x33
+#define OP_lit4 0x34
+#define OP_lit5 0x35
+#define OP_lit6 0x36
+#define OP_lit7 0x37
+#define OP_lit8 0x38
+#define OP_lit9 0x39
+#define OP_lit10 0x3a
+#define OP_lit11 0x3b
+#define OP_lit12 0x3c
+#define OP_lit13 0x3d
+#define OP_lit14 0x3e
+#define OP_lit15 0x3f
+#define OP_lit16 0x40
+#define OP_lit17 0x41
+#define OP_lit18 0x42
+#define OP_lit19 0x43
+#define OP_lit20 0x44
+#define OP_lit21 0x45
+#define OP_lit22 0x46
+#define OP_lit23 0x47
+#define OP_lit24 0x48
+#define OP_lit25 0x49
+#define OP_lit26 0x4a
+#define OP_lit27 0x4b
+#define OP_lit28 0x4c
+#define OP_lit29 0x4d
+#define OP_lit30 0x4e
+#define OP_lit31 0x4f
+#define OP_reg0 0x50
+#define OP_reg1 0x51
+#define OP_reg2 0x52
+#define OP_reg3 0x53
+#define OP_reg4 0x54
+#define OP_reg5 0x55
+#define OP_reg6 0x56
+#define OP_reg7 0x57
+#define OP_reg8 0x58
+#define OP_reg9 0x59
+#define OP_reg10 0x5a
+#define OP_reg11 0x5b
+#define OP_reg12 0x5c
+#define OP_reg13 0x5d
+#define OP_reg14 0x5e
+#define OP_reg15 0x5f
+#define OP_reg16 0x60
+#define OP_reg17 0x61
+#define OP_reg18 0x62
+#define OP_reg19 0x63
+#define OP_reg20 0x64
+#define OP_reg21 0x65
+#define OP_reg22 0x66
+#define OP_reg23 0x67
+#define OP_reg24 0x68
+#define OP_reg25 0x69
+#define OP_reg26 0x6a
+#define OP_reg27 0x6b
+#define OP_reg28 0x6c
+#define OP_reg29 0x6d
+#define OP_reg30 0x6e
+#define OP_reg31 0x6f
+#define OP_breg0 0x70
+#define OP_breg1 0x71
+#define OP_breg2 0x72
+#define OP_breg3 0x73
+#define OP_breg4 0x74
+#define OP_breg5 0x75
+#define OP_breg6 0x76
+#define OP_breg7 0x77
+#define OP_breg8 0x78
+#define OP_breg9 0x79
+#define OP_breg10 0x7a
+#define OP_breg11 0x7b
+#define OP_breg12 0x7c
+#define OP_breg13 0x7d
+#define OP_breg14 0x7e
+#define OP_breg15 0x7f
+#define OP_breg16 0x80
+#define OP_breg17 0x81
+#define OP_breg18 0x82
+#define OP_breg19 0x83
+#define OP_breg20 0x84
+#define OP_breg21 0x85
+#define OP_breg22 0x86
+#define OP_breg23 0x87
+#define OP_breg24 0x88
+#define OP_breg25 0x89
+#define OP_breg26 0x8a
+#define OP_breg27 0x8b
+#define OP_breg28 0x8c
+#define OP_breg29 0x8d
+#define OP_breg30 0x8e
+#define OP_breg31 0x8f
+#define OP_regx 0x90
+#define OP_fbreg 0x91
+#define OP_bregx 0x92
+#define OP_piece 0x93
+#define OP_deref_size 0x94
+#define OP_xderef_size 0x95
+#define OP_nop 0x96
+#define OP_push_object_address 0x97
+#define OP_call2 0x98
+#define OP_call4 0x99
+#define OP_calli 0x9a /* typo? */
+#define OP_ref 0x9a
+#define OP_call_ref 0x9a
+#define OP_call_frame_cfa 0x9c
+#define OP_bit_piece 0x9d
+#define OP_lo_user 0xe0
+#define OP_hi_user 0xff
+
+#define FT_char 0x0001
+#define FT_signed_char 0x0002
+#define FT_unsigned_char 0x0003
+#define FT_short 0x0004
+#define FT_signed_short 0x0005
+#define FT_unsigned_short 0x0006
+#define FT_integer 0x0007
+#define FT_signed_integer 0x0008
+#define FT_unsigned_integer 0x0009
+#define FT_long 0x000a
+#define FT_signed_long 0x000b
+#define FT_unsigned_long 0x000c
+#define FT_pointer 0x000d
+#define FT_float 0x000e
+#define FT_dbl_prec_float 0x000f
+#define FT_ext_prec_float 0x0010
+#define FT_complex 0x0011
+#define FT_dbl_prec_complex 0x0012
+#define FT_void 0x0014
+#define FT_boolean 0x0015
+#define FT_ext_prec_complex 0x0016
+#define FT_label 0x0017
+#define FT_lo_user 0x8000
+#define FT_hi_user 0xffff
+#define FT_longlong 0x8008
+#define FT_signed_longlong 0x8108
+#define FT_unsigned_longlong 0x8208
+#define FT_vector_signed_char 0xa002
+#define FT_vector_unsigned_char 0xa003
+#define FT_vector_signed_short 0xa005
+#define FT_vector_unsigned_short 0xa006
+#define FT_vector_signed_int 0xa008
+#define FT_vector_unsigned_int 0xa009
+#define FT_vector_float 0xa00e
+#define FT_ev64_s16 0xb005
+#define FT_ev64_u16 0xb006
+#define FT_ev64_s32 0xb008
+#define FT_ev64_u32 0xb009
+#define FT_ev64_s64 0xb208
+#define FT_ev64_u64 0xb209
+#define FT_ev64_fs 0xb00e
+#define FT_ev64_opaque 0xb020
+
+#define MOD_pointer_to 0x01
+#define MOD_reference_to 0x02
+#define MOD_const 0x03
+#define MOD_volatile 0x04
+#define MOD_lo_user 0x80
+#define MOD_hi_user 0xff
+
+#define LANG_C89 0x00000001
+#define LANG_C 0x00000002
+#define LANG_ADA83 0x00000003
+#define LANG_C_PLUS_PLUS 0x00000004
+#define LANG_COBOL74 0x00000005
+#define LANG_COBOL85 0x00000006
+#define LANG_FORTRAN77 0x00000007
+#define LANG_FORTRAN90 0x00000008
+#define LANG_PASCAL83 0x00000009
+#define LANG_MODULA2 0x0000000a
+#define LANG_JAVA 0x0000000b /* v3 */
+#define LANG_C99 0x0000000c /* v3 */
+#define LANG_ADA95 0x0000000d /* v3 */
+#define LANG_FORTRAN95 0x0000000e /* v3 */
+#define LANG_PLI 0x0000000f
+#define LANG_lo_user 0x00008000
+#define LANG_hi_user 0x0000ffff
+
+#define ORD_row_major 0
+#define ORD_col_major 1
+
+#define FMT_FT_C_C 0x0
+#define FMT_FT_C_X 0x1
+#define FMT_FT_X_C 0x2
+#define FMT_FT_X_X 0x3
+#define FMT_UT_C_C 0x4
+#define FMT_UT_C_X 0x5
+#define FMT_UT_X_C 0x6
+#define FMT_UT_X_X 0x7
+#define FMT_ET 0x8
+
+#define ATE_address 0x01
+#define ATE_boolean 0x02
+#define ATE_complex_float 0x03
+#define ATE_float 0x04
+#define ATE_signed 0x05
+#define ATE_signed_char 0x06
+#define ATE_unsigned 0x07
+#define ATE_unsigned_char 0x08
+#define ATE_imaginary_float 0x09 /* v3 */
+#define ATE_lo_user 0x80
+#define ATE_hi_user 0xff
+
+#define DW_LNS_copy 1
+#define DW_LNS_advance_pc 2
+#define DW_LNS_advance_line 3
+#define DW_LNS_set_file 4
+#define DW_LNS_set_column 5
+#define DW_LNS_negate_stmt 6
+#define DW_LNS_set_basic_block 7
+#define DW_LNS_const_add_pc 8
+#define DW_LNS_fixed_advance_pc 9
+#define DW_LNS_set_prologue_end 0xa /* v3 */
+#define DW_LNS_set_epilogue_begin 0xb /* v3 */
+#define DW_LNS_set_isa 0xc /* v3 */
+#define DW_LNS_expected_opcode_base 0xd /* highest standard opcode plus one */
+#define DW_LNS_vendor_extension 0x100
+#define DW_LNS_special_opcode 0x101
+
+#define DW_LNE_end_sequence 1
+#define DW_LNE_set_address 2
+#define DW_LNE_define_file 3
+#define DW_LNE_lo_user 0x80 /* v3 */
+#define DW_LNE_hi_user 0xff /* v3 */
+
+#define ACCESS_public 1
+#define ACCESS_protected 2
+#define ACCESS_private 3
+
+#define VIS_local 1
+#define VIS_exported 2
+#define VIS_qualified 3
+
+#define VIRTUALITY_none 0
+#define VIRTUALITY_virtual 1
+#define VIRTUALITY_pure_virtual 2
+
+#define ID_case_sensitive 0
+#define ID_up_case 1
+#define ID_down_case 2
+#define ID_case_insensitive 3
+
+#define CC_normal 0x01
+#define CC_program 0x02
+#define CC_nocall 0x03
+#define CC_lo_user 0x40
+#define CC_hi_user 0xff
+
+#define INL_not_inlined 0
+#define INL_inlined 1
+#define INL_declared_not_inlined 2
+#define INL_declared_inlined 3
+
+#define DSC_label 0
+#define DSC_range 1
+
+#define MACINFO_define 1
+#define MACINFO_undef 2
+#define MACINFO_start_file 3
+#define MACINFO_end_file 4
+#define MACINFO_vendor_ext 0xff
+
+/* The following three defines represent */
+/* the high 2 bits only. */
+#define CFA_advance_loc 0x01
+#define CFA_offset 0x02
+#define CFA_restore 0x03
+
+#define CFA_nop 0x00
+#define CFA_set_loc 0x01
+#define CFA_advance_loc1 0x02
+#define CFA_advance_loc2 0x03
+#define CFA_advance_loc4 0x04
+#define CFA_offset_extended 0x05
+#define CFA_restore_extended 0x06
+#define CFA_undefined 0x07
+#define CFA_same_value 0x08
+#define CFA_register 0x09
+#define CFA_remember_state 0x0a
+#define CFA_restore_state 0x0b
+#define CFA_def_cfa 0x0c
+#define CFA_def_cfa_register 0x0d
+#define CFA_def_cfa_offset 0x0e
+#define CFA_def_cfa_expression 0x0f
+#define CFA_expression 0x10 /* v3 */
+#define CFA_offset_extended_sf 0x11 /* v3 */
+#define CFA_def_cfa_sf 0x12 /* v3 */
+#define CFA_def_cfa_offset_sf 0x13 /* v3 */
+#define CFA_lo_user 0x1c
+#define CFA_hi_user 0x3f
+
+
+#define ADDR_none 0
+#define ADDR_near16 1
+#define ADDR_far16 2
+#define ADDR_huge16 3
+#define ADDR_near32 4
+#define ADDR_far32 5
+
+#define DW_END_default 0x00
+#define DW_END_big 0x01
+#define DW_END_little 0x02
+#define DW_END_lo_user 0x40
+#define DW_END_hi_user 0xff
diff --git a/agent/tcf/services/dwarfcache.c b/agent/tcf/services/dwarfcache.c
new file mode 100644
index 00000000..0da30798
--- /dev/null
+++ b/agent/tcf/services/dwarfcache.c
@@ -0,0 +1,1345 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+
+/*
+ * This module implements caching of DWARF debug information.
+ *
+ * Functions in this module use exceptions to report errors, see exceptions.h
+ */
+
+#include <config.h>
+
+#if ENABLE_ELF && ENABLE_DebugContext
+
+#include <assert.h>
+#include <framework/exceptions.h>
+#include <framework/myalloc.h>
+#include <services/dwarf.h>
+#include <services/dwarfio.h>
+#include <services/dwarfcache.h>
+#include <services/dwarfexpr.h>
+#include <services/stacktrace.h>
+
+#define OBJ_HASH(Cache,ID) (((U4_T)(ID) + ((U4_T)(ID) >> 8)) % Cache->mObjectHashSize)
+
+static DWARFCache * sCache;
+static ELF_Section * sDebugSection;
+static DIO_UnitDescriptor sUnitDesc;
+static CompUnit * sCompUnit;
+static ObjectInfo * sParentObject;
+static ObjectInfo * sPrevSibling;
+
+static int sCloseListenerOK = 0;
+
+unsigned calc_file_name_hash(const char * s) {
+ unsigned l = strlen(s);
+ unsigned h = 0;
+ while (l > 0) {
+ unsigned g;
+ unsigned char ch = s[--l];
+ if (ch == '/') break;
+ if (ch == '\\') break;
+ h = (h << 4) + ch;
+ g = h & 0xf0000000;
+ if (g) h ^= g >> 24;
+ h &= ~g;
+ }
+ return h;
+}
+
+ObjectInfo * find_object(DWARFCache * Cache, U8_T ID) {
+ if (Cache->mObjectHash != NULL) {
+ ObjectInfo * Info = Cache->mObjectHash[OBJ_HASH(Cache, ID)];
+
+ while (Info != NULL) {
+ if (Info->mID == ID) return Info;
+ Info = Info->mHashNext;
+ }
+ }
+ return NULL;
+}
+
+static ObjectInfo * add_object_info(U8_T ID) {
+ ObjectInfo * Info = find_object(sCache, ID);
+ if (Info == NULL) {
+ U4_T Hash;
+ if (ID < sDebugSection->addr + dio_gEntryPos) str_exception(ERR_INV_DWARF, "Invalid entry reference");
+ if (ID > sDebugSection->addr + sDebugSection->size) str_exception(ERR_INV_DWARF, "Invalid entry reference");
+ if (sCache->mObjectHash == NULL) {
+ sCache->mObjectHashSize = (unsigned)(sDebugSection->size / 251);
+ if (sCache->mObjectHashSize < 101) sCache->mObjectHashSize = 101;
+ sCache->mObjectHash = (ObjectInfo **)loc_alloc_zero(sizeof(ObjectInfo *) * sCache->mObjectHashSize);
+ }
+ Hash = OBJ_HASH(sCache, ID);
+ if (sCache->mObjectArrayPos >= OBJECT_ARRAY_SIZE) {
+ ObjectArray * Buf = (ObjectArray *)loc_alloc_zero(sizeof(ObjectArray));
+ Buf->mNext = sCache->mObjectList;
+ sCache->mObjectList = Buf;
+ sCache->mObjectArrayPos = 0;
+ }
+ Info = sCache->mObjectList->mArray + sCache->mObjectArrayPos++;
+ Info->mHashNext = sCache->mObjectHash[Hash];
+ sCache->mObjectHash[Hash] = Info;
+ Info->mID = ID;
+ }
+ return Info;
+}
+
+static CompUnit * add_comp_unit(U8_T ID) {
+ ObjectInfo * Info = add_object_info(ID);
+ if (Info->mCompUnit == NULL) {
+ CompUnit * Unit = (CompUnit *)loc_alloc_zero(sizeof(CompUnit));
+ Unit->mObject = Info;
+ Info->mCompUnit = Unit;
+ }
+ return Info->mCompUnit;
+}
+
+static U4_T get_fund_type_size(CompUnit * Unit, U2_T ft) {
+ switch (ft) {
+ case FT_char :
+ case FT_signed_char :
+ case FT_unsigned_char :
+ return 1;
+ case FT_short :
+ case FT_signed_short :
+ case FT_unsigned_short:
+ return 2;
+ case FT_integer :
+ case FT_signed_integer:
+ case FT_unsigned_integer:
+ return 4;
+ case FT_long :
+ case FT_signed_long :
+ case FT_unsigned_long :
+ return Unit->mFile->elf64 ? 8 : 4;
+ case FT_pointer :
+ return Unit->mDesc.mAddressSize;
+ case FT_float :
+ return 4;
+ case FT_dbl_prec_float:
+ return 8;
+ case FT_complex :
+ return 8;
+ case FT_dbl_prec_complex:
+ return 16;
+ case FT_boolean :
+ return 4;
+ case FT_void :
+ return 0;
+ }
+ str_exception(ERR_INV_DWARF, "Invalid fundamental type code");
+ return 0;
+}
+
+static void read_mod_fund_type(U2_T Form, ObjectInfo ** Type) {
+ U1_T * Buf;
+ size_t BufSize;
+ size_t BufPos;
+ int i;
+ U2_T FT = 0;
+ dio_ChkBlock(Form, &Buf, &BufSize);
+ for (i = 0; i < 2; i++) {
+ FT |= (U2_T)Buf[BufSize - 2 +
+ (sDebugSection->file->big_endian ? 1 - i : i)] << (i * 8);
+ }
+ *Type = add_object_info(sDebugSection->addr + dio_GetPos() - 2);
+ (*Type)->mTag = TAG_fund_type;
+ (*Type)->mCompUnit = sCompUnit;
+ (*Type)->u.mFundType = FT;
+ BufPos = BufSize - 2;
+ while (BufPos > 0) {
+ U2_T Tag = 0;
+ ObjectInfo * Mod = NULL;
+ switch (Buf[--BufPos]) {
+ case MOD_volatile:
+ case MOD_const:
+ continue;
+ case MOD_pointer_to:
+ Tag = TAG_mod_pointer;
+ break;
+ case MOD_reference_to:
+ Tag = TAG_mod_reference;
+ break;
+ default:
+ str_exception(ERR_INV_DWARF, "Invalid type modifier code");
+ }
+ Mod = add_object_info(sDebugSection->addr + dio_GetPos() - BufSize + BufPos);
+ Mod->mTag = Tag;
+ Mod->mCompUnit = sCompUnit;
+ Mod->mType = *Type;
+ *Type = Mod;
+ }
+}
+
+static void read_mod_user_def_type(U2_T Form, ObjectInfo ** Type) {
+ U1_T * Buf;
+ size_t BufSize;
+ size_t BufPos;
+ int i;
+ U4_T Ref = 0;
+ dio_ChkBlock(Form, &Buf, &BufSize);
+ for (i = 0; i < 4; i++) {
+ Ref |= (U4_T)Buf[BufSize - 4 +
+ (sDebugSection->file->big_endian ? 3 - i : i)] << (i * 8);
+ }
+ *Type = add_object_info(sDebugSection->addr + Ref);
+ BufPos = BufSize - 4;
+ while (BufPos > 0) {
+ U2_T Tag = 0;
+ ObjectInfo * Mod = NULL;
+ switch (Buf[--BufPos]) {
+ case MOD_volatile:
+ case MOD_const:
+ continue;
+ case MOD_pointer_to:
+ Tag = TAG_mod_pointer;
+ break;
+ case MOD_reference_to:
+ Tag = TAG_mod_reference;
+ break;
+ default:
+ str_exception(ERR_INV_DWARF, "Invalid type modifier code");
+ }
+ Mod = add_object_info(sDebugSection->addr + dio_GetPos() - BufSize + BufPos);
+ Mod->mTag = Tag;
+ Mod->mCompUnit = sCompUnit;
+ Mod->mType = *Type;
+ *Type = Mod;
+ }
+}
+
+static I8_T read_long_value(void) {
+ switch (get_fund_type_size(sCompUnit, FT_long)) {
+ case 4: return (I4_T)dio_ReadU4();
+ case 8: return (I8_T)dio_ReadU8();
+ }
+ str_exception(ERR_OTHER, "Invalid size of long int");
+ return 0;
+}
+
+static void read_subscr_data(U2_T Form, ObjectInfo * Array) {
+ U1_T * Buf;
+ size_t BufSize;
+ U8_T BufEnd = 0;
+ U8_T OrgPos = dio_GetPos();
+ ObjectInfo ** Children = &Array->mChildren;
+
+ assert(Array->mChildren == NULL);
+ assert(Array->mType == NULL);
+
+ dio_ChkBlock(Form, &Buf, &BufSize);
+ dio_SetPos(Buf - (U1_T *)sDebugSection->data);
+ BufEnd = dio_GetPos() + BufSize;
+ while (dio_GetPos() < BufEnd) {
+ ObjectInfo * Type = NULL;
+ U1_T Fmt = dio_ReadU1();
+ switch (Fmt) {
+ case FMT_FT_C_C:
+ case FMT_FT_C_X:
+ case FMT_FT_X_C:
+ case FMT_FT_X_X:
+ Type = add_object_info(sDebugSection->addr + dio_GetPos());
+ Type->mTag = TAG_fund_type;
+ Type->mCompUnit = sCompUnit;
+ Type->u.mFundType = dio_ReadU2();
+ break;
+ case FMT_UT_C_C:
+ case FMT_UT_C_X:
+ case FMT_UT_X_C:
+ case FMT_UT_X_X:
+ dio_ReadAttribute(AT_subscr_data, FORM_REF);
+ Type = add_object_info(dio_gFormData);
+ break;
+ }
+ if (Type != NULL) {
+ ObjectInfo * Range = add_object_info(sDebugSection->addr + dio_GetPos());
+ Range->mTag = TAG_index_range;
+ Range->mCompUnit = sCompUnit;
+ Range->mType = Type;
+ Range->u.mRange.mFmt = Fmt;
+ switch (Fmt) {
+ case FMT_FT_C_C:
+ case FMT_FT_C_X:
+ case FMT_UT_C_C:
+ case FMT_UT_C_X:
+ Range->u.mRange.mLow.mValue = read_long_value();
+ break;
+ case FMT_FT_X_C:
+ case FMT_FT_X_X:
+ case FMT_UT_X_C:
+ case FMT_UT_X_X:
+ dio_ReadAttribute(0, FORM_BLOCK2);
+ Range->u.mRange.mLow.mExpr.mAddr = (U1_T *)dio_gFormDataAddr;
+ Range->u.mRange.mLow.mExpr.mSize = dio_gFormDataSize;
+ break;
+ }
+ switch (Fmt) {
+ case FMT_FT_C_C:
+ case FMT_FT_X_C:
+ case FMT_UT_C_C:
+ case FMT_UT_X_C:
+ Range->u.mRange.mHigh.mValue = read_long_value();
+ break;
+ case FMT_FT_C_X:
+ case FMT_FT_X_X:
+ case FMT_UT_C_X:
+ case FMT_UT_X_X:
+ dio_ReadAttribute(0, FORM_BLOCK2);
+ Range->u.mRange.mHigh.mExpr.mAddr = (U1_T *)dio_gFormDataAddr;
+ Range->u.mRange.mHigh.mExpr.mSize = dio_gFormDataSize;
+ break;
+ }
+ *Children = Range;
+ Children = &Range->mSibling;
+ }
+ else if (Fmt == FMT_ET) {
+ U2_T x = dio_ReadU2();
+ U2_T Attr = (x & 0xfff0u) >> 4;
+ U2_T Form = x & 0xfu;
+ dio_ReadAttribute(Attr, Form);
+ switch (Attr) {
+ case AT_fund_type:
+ dio_ChkData(Form);
+ Type = add_object_info(sDebugSection->addr + dio_GetPos() - dio_gFormDataSize);
+ Type->mTag = TAG_fund_type;
+ Type->mCompUnit = sCompUnit;
+ Type->u.mFundType = (U2_T)dio_gFormData;
+ break;
+ case AT_user_def_type:
+ dio_ChkRef(Form);
+ Type = add_object_info(dio_gFormData);
+ break;
+ case AT_mod_fund_type:
+ read_mod_fund_type(Form, &Type);
+ break;
+ case AT_mod_u_d_type:
+ read_mod_user_def_type(Form, &Type);
+ break;
+ default:
+ str_exception(ERR_INV_DWARF, "Invalid array element type format");
+ }
+ Array->mType = Type;
+ }
+ else {
+ str_exception(ERR_INV_DWARF, "Invalid array subscription format");
+ }
+ }
+ dio_SetPos(OrgPos);
+}
+
+static void read_object_info(U2_T Tag, U2_T Attr, U2_T Form) {
+ static ObjectInfo * Info;
+ static ObjectInfo * Spec;
+ static ObjectInfo * AOrg;
+ static U8_T Sibling;
+ static int HasChildren;
+
+ switch (Attr) {
+ case 0:
+ if (Form) {
+ assert((sParentObject == NULL) == (Tag == TAG_compile_unit));
+ if (Tag == TAG_compile_unit) {
+ CompUnit * Unit = add_comp_unit(sDebugSection->addr + dio_gEntryPos);
+ Unit->mFile = sCache->mFile;
+ Unit->mDebugRangesOffs = ~(U8_T)0;
+ Unit->mRegIdScope.big_endian = sCache->mFile->big_endian;
+ Unit->mRegIdScope.machine = sCache->mFile->machine;
+ Unit->mRegIdScope.os_abi = sCache->mFile->os_abi;
+ Unit->mRegIdScope.id_type = REGNUM_DWARF;
+ Info = Unit->mObject;
+ sCompUnit = Unit;
+ }
+ else {
+ Info = add_object_info(sDebugSection->addr + dio_gEntryPos);
+ Info->mCompUnit = sCompUnit;
+ }
+ assert(Info->mTag == 0);
+ Info->mTag = Tag;
+ Info->mParent = sParentObject;
+ HasChildren = Form == DWARF_ENTRY_HAS_CHILDREN;
+ Sibling = 0;
+ Spec = NULL;
+ AOrg = NULL;
+ }
+ else {
+ if (Spec != NULL) {
+ if (Info->mName == NULL) Info->mName = Spec->mName;
+ if (Info->mType == NULL) Info->mType = Spec->mType;
+ }
+ if (AOrg != NULL) {
+ if (Info->mName == NULL) Info->mName = AOrg->mName;
+ if (Info->mType == NULL) Info->mType = AOrg->mType;
+ }
+ if (Tag == TAG_compile_unit && Sibling == 0) Sibling = sUnitDesc.mUnitOffs + sUnitDesc.mUnitSize;
+ if (Tag == TAG_enumerator && Info->mType == NULL) Info->mType = sParentObject;
+ if (sPrevSibling != NULL) sPrevSibling->mSibling = Info;
+ else if (sParentObject != NULL) sParentObject->mChildren = Info;
+ else sCache->mCompUnits = Info;
+ sPrevSibling = Info;
+ if (Sibling != 0 || HasChildren) {
+ U8_T SiblingPos = Sibling;
+ ObjectInfo * Parent = sParentObject;
+ ObjectInfo * PrevSibling = sPrevSibling;
+ sParentObject = Info;
+ sPrevSibling = NULL;
+ for (;;) {
+ if (SiblingPos > 0 && dio_GetPos() >= SiblingPos) break;
+ if (!dio_ReadEntry(read_object_info, 0)) break;
+ }
+ if (SiblingPos > dio_GetPos()) dio_SetPos(SiblingPos);
+ sParentObject = Parent;
+ sPrevSibling = PrevSibling;
+ }
+ }
+ break;
+ case AT_sibling:
+ dio_ChkRef(Form);
+ Sibling = dio_gFormData - sDebugSection->addr;
+ break;
+ case AT_type:
+ dio_ChkRef(Form);
+ Info->mType = add_object_info(dio_gFormData);
+ break;
+ case AT_fund_type:
+ dio_ChkData(Form);
+ Info->mType = add_object_info(sDebugSection->addr + dio_GetPos() - dio_gFormDataSize);
+ Info->mType->mTag = TAG_fund_type;
+ Info->mType->mCompUnit = sCompUnit;
+ Info->mType->u.mFundType = (U2_T)dio_gFormData;
+ break;
+ case AT_user_def_type:
+ dio_ChkRef(Form);
+ Info->mType = add_object_info(dio_gFormData);
+ break;
+ case AT_mod_fund_type:
+ read_mod_fund_type(Form, &Info->mType);
+ break;
+ case AT_mod_u_d_type:
+ read_mod_user_def_type(Form, &Info->mType);
+ break;
+ case AT_subscr_data:
+ read_subscr_data(Form, Info);
+ break;
+ case AT_name:
+ dio_ChkString(Form);
+ Info->mName = (char *)dio_gFormDataAddr;
+ break;
+ case AT_specification_v2:
+ dio_ChkRef(Form);
+ Spec = add_object_info(dio_gFormData);
+ break;
+ case AT_abstract_origin:
+ dio_ChkRef(Form);
+ AOrg = add_object_info(dio_gFormData);
+ break;
+ case AT_low_pc:
+ dio_ChkAddr(Form);
+ Info->u.mAddr.mLowPC = (ContextAddress)dio_gFormData;
+ break;
+ case AT_high_pc:
+ dio_ChkAddr(Form);
+ Info->u.mAddr.mHighPC = (ContextAddress)dio_gFormData;
+ break;
+ }
+ if (Tag == TAG_compile_unit) {
+ CompUnit * Unit = Info->mCompUnit;
+ switch (Attr) {
+ case AT_low_pc:
+ dio_ChkAddr(Form);
+ Unit->mLowPC = (ContextAddress)dio_gFormData;
+ Unit->mTextSection = dio_gFormSection;
+ break;
+ case AT_high_pc:
+ dio_ChkAddr(Form);
+ Unit->mHighPC = (ContextAddress)dio_gFormData;
+ break;
+ case AT_ranges:
+ dio_ChkData(Form);
+ Unit->mDebugRangesOffs = dio_gFormData;
+ break;
+ case AT_comp_dir:
+ dio_ChkString(Form);
+ Unit->mDir = (char *)dio_gFormDataAddr;
+ break;
+ case AT_stmt_list:
+ dio_ChkData(Form);
+ Unit->mLineInfoOffs = dio_gFormData;
+ break;
+ case AT_base_types:
+ Unit->mBaseTypes = add_comp_unit(dio_gFormData);
+ break;
+ case AT_language:
+ dio_ChkData(Form);
+ Unit->mLanguage = (U2_T)dio_gFormData;
+ break;
+ }
+ }
+}
+
+static int cmp_addr_ranges(const void * x, const void * y) {
+ UnitAddressRange * rx = (UnitAddressRange *)x;
+ UnitAddressRange * ry = (UnitAddressRange *)y;
+ if (rx->mAddr < ry->mAddr) return -1;
+ if (rx->mAddr > ry->mAddr) return +1;
+ return 0;
+}
+
+static void add_addr_range(ELF_Section * sec, CompUnit * unit, ContextAddress addr, ContextAddress size) {
+ UnitAddressRange * range = NULL;
+ if (sCache->mAddrRangesCnt >= sCache->mAddrRangesMax) {
+ sCache->mAddrRangesMax = sCache->mAddrRangesMax == 0 ? 64 : sCache->mAddrRangesMax * 2;
+ sCache->mAddrRanges = (UnitAddressRange *)loc_realloc(sCache->mAddrRanges, sizeof(UnitAddressRange) * sCache->mAddrRangesMax);
+ }
+ range = sCache->mAddrRanges + sCache->mAddrRangesCnt++;
+ memset(range, 0, sizeof(UnitAddressRange));
+ range->mSection = sec;
+ range->mAddr = addr;
+ range->mSize = size;
+ range->mUnit = unit;
+}
+
+static void load_addr_ranges(void) {
+ Trap trap;
+ unsigned idx;
+ ELF_File * file = sCache->mFile;
+ ELF_Section * debug_ranges = NULL;
+
+ memset(&trap, 0, sizeof(trap));
+ for (idx = 1; idx < file->section_cnt; idx++) {
+ ELF_Section * sec = file->sections + idx;
+ if (sec->size == 0) continue;
+ if (sec->name == NULL) continue;
+ if (strcmp(sec->name, ".debug_ranges") == 0) {
+ debug_ranges = sec;
+ }
+ else if (strcmp(sec->name, ".debug_aranges") == 0) {
+ ObjectInfo * info = sCache->mCompUnits;
+ dio_EnterSection(NULL, sec, 0);
+ if (set_trap(&trap)) {
+ while (dio_GetPos() < sec->size) {
+ int dwarf64 = 0;
+ U8_T size = dio_ReadU4();
+ U8_T next = 0;
+ if (size == 0xffffffffu) {
+ dwarf64 = 1;
+ size = dio_ReadU8();
+ }
+ next = dio_GetPos() + size;
+ if (dio_ReadU2() != 2) {
+ dio_SetPos(next);
+ }
+ else {
+ U8_T offs = dwarf64 ? dio_ReadU8() : (U8_T)dio_ReadU4();
+ U1_T addr_size = dio_ReadU1();
+ U1_T segm_size = dio_ReadU1();
+ if (segm_size != 0) str_exception(ERR_INV_DWARF, "segment descriptors are not supported");
+ while (info != NULL && info->mCompUnit->mDesc.mUnitOffs != offs) info = info->mSibling;
+ if (info == NULL) {
+ info = sCache->mCompUnits;
+ while (info != NULL && info->mCompUnit->mDesc.mUnitOffs != offs) info = info->mSibling;
+ }
+ if (info == NULL) str_exception(ERR_INV_DWARF, "invalid .debug_aranges section");
+ info->mCompUnit->mARangesFound = 1;
+ while (dio_GetPos() % (addr_size * 2) != 0) dio_Skip(1);
+ for (;;) {
+ ELF_Section * range_sec = NULL;
+ ContextAddress addr = dio_ReadAddressX(&range_sec, addr_size);
+ ContextAddress size = dio_ReadUX(addr_size);
+ if (addr == 0 && size == 0) break;
+ if (size == 0) continue;
+ add_addr_range(range_sec, info->mCompUnit, addr, size);
+ }
+ }
+ }
+ clear_trap(&trap);
+ }
+ dio_ExitSection();
+ if (trap.error) break;
+ }
+ }
+ if (trap.error) exception(trap.error);
+ if (sCache->mCompUnits != NULL) {
+ ObjectInfo * info = sCache->mCompUnits;
+ while (info != NULL) {
+ CompUnit * unit = info->mCompUnit;
+ ContextAddress base = unit->mLowPC;
+ ContextAddress size = unit->mHighPC - unit->mLowPC;
+ info = info->mSibling;
+ if (unit->mARangesFound) continue;
+ if (size == 0) continue;
+ if (unit->mDebugRangesOffs != ~(U8_T)0 && debug_ranges != NULL) {
+ dio_EnterSection(&unit->mDesc, debug_ranges, unit->mDebugRangesOffs);
+ for (;;) {
+ ELF_Section * sec = NULL;
+ U8_T x = dio_ReadAddress(&sec);
+ U8_T y = dio_ReadAddress(&sec);
+ if (x == 0 && y == 0) break;
+ if (sec != unit->mTextSection) exception(ERR_INV_DWARF);
+ if (x == ((U8_T)1 << unit->mDesc.mAddressSize * 8) - 1) {
+ base = (ContextAddress)y;
+ }
+ else {
+ x = base + x;
+ y = base + y;
+ add_addr_range(sec, unit, x, y - x);
+ }
+ }
+ dio_ExitSection();
+ }
+ else {
+ add_addr_range(unit->mTextSection, unit, base, size);
+ }
+ }
+ }
+ if (sCache->mAddrRangesCnt > 1) {
+ qsort(sCache->mAddrRanges, sCache->mAddrRangesCnt, sizeof(UnitAddressRange), cmp_addr_ranges);
+ }
+}
+
+static void load_pub_names(ELF_Section * debug_info, ELF_Section * pub_names, PubNamesTable * tbl) {
+ tbl->mMax = (unsigned)(pub_names->size / 16) + 16;
+ tbl->mHash = (unsigned *)loc_alloc_zero(sizeof(unsigned) * SYM_HASH_SIZE);
+ tbl->mNext = (PubNamesInfo *)loc_alloc(sizeof(PubNamesInfo) * tbl->mMax);
+ memset(tbl->mNext + tbl->mCnt++, 0, sizeof(PubNamesInfo));
+ dio_EnterSection(NULL, pub_names, 0);
+ while (dio_GetPos() < pub_names->size) {
+ int dwarf64 = 0;
+ U8_T size = dio_ReadU4();
+ U8_T next = 0;
+ if (size == 0xffffffffu) {
+ dwarf64 = 1;
+ size = dio_ReadU8();
+ }
+ next = dio_GetPos() + size;
+ if (dio_ReadU2() == 2) {
+ ELF_Section * unit_sect = NULL;
+ U8_T unit_addr = dio_ReadAddressX(&unit_sect, dwarf64 ? 8 : 4);
+ U8_T unit_offs = unit_sect == NULL ? unit_addr : unit_addr - unit_sect->addr;
+ U8_T unit_size = dwarf64 ? dio_ReadU8() : (U8_T)dio_ReadU4();
+ if (unit_offs + unit_size > debug_info->size) str_fmt_exception(ERR_INV_DWARF,
+ "Invalid unit size in %s section", pub_names->name);
+ for (;;) {
+ unsigned h;
+ PubNamesInfo * info = NULL;
+ U8_T obj_offs = dwarf64 ? dio_ReadU8() : (U8_T)dio_ReadU4();
+ if (obj_offs == 0) break;
+ if (obj_offs >= unit_size) str_fmt_exception(ERR_INV_DWARF,
+ "Invalid object offset in %s section", pub_names->name);
+ if (tbl->mCnt >= tbl->mMax) {
+ tbl->mMax = tbl->mMax * 3 / 2;
+ tbl->mNext = (PubNamesInfo *)loc_realloc(tbl->mNext, sizeof(PubNamesInfo) * tbl->mMax);
+ }
+ info = tbl->mNext + tbl->mCnt;
+ h = calc_symbol_name_hash(dio_ReadString());
+ info->mID = debug_info->addr + unit_offs + obj_offs;
+ info->mNext = tbl->mHash[h];
+ tbl->mHash[h] = tbl->mCnt++;
+ }
+ }
+ assert(next >= dio_GetPos());
+ dio_SetPos(next);
+ }
+ dio_ExitSection();
+}
+
+static void load_debug_sections(void) {
+ Trap trap;
+ unsigned idx;
+ ELF_Section * pub_names = NULL;
+ ELF_Section * pub_types = NULL;
+ ELF_Section * debug_info = NULL;
+ ELF_File * file = sCache->mFile;
+
+ memset(&trap, 0, sizeof(trap));
+
+ for (idx = 1; idx < file->section_cnt; idx++) {
+ ELF_Section * sec = file->sections + idx;
+ if (sec->size == 0) continue;
+ if (sec->name == NULL) continue;
+ if (strcmp(sec->name, ".debug") == 0 || strcmp(sec->name, ".debug_info") == 0) {
+ if (strcmp(sec->name, ".debug_info") == 0) debug_info = sec;
+ sDebugSection = sec;
+ sParentObject = NULL;
+ sPrevSibling = NULL;
+ dio_EnterSection(NULL, sec, 0);
+ if (set_trap(&trap)) {
+ while (dio_GetPos() < sec->size) {
+ dio_ReadUnit(&sUnitDesc, read_object_info);
+ sCompUnit->mDesc = sUnitDesc;
+ }
+ clear_trap(&trap);
+ }
+ dio_ExitSection();
+ sParentObject = NULL;
+ sPrevSibling = NULL;
+ sCompUnit = NULL;
+ sDebugSection = NULL;
+ if (trap.error) break;
+ }
+ else if (strcmp(sec->name, ".line") == 0) {
+ sCache->mDebugLineV1 = sec;
+ }
+ else if (strcmp(sec->name, ".debug_line") == 0) {
+ sCache->mDebugLine = sec;
+ }
+ else if (strcmp(sec->name, ".debug_loc") == 0) {
+ sCache->mDebugLoc = sec;
+ }
+ else if (strcmp(sec->name, ".debug_ranges") == 0) {
+ sCache->mDebugRanges = sec;
+ }
+ else if (strcmp(sec->name, ".debug_frame") == 0) {
+ sCache->mDebugFrame = sec;
+ }
+ else if (strcmp(sec->name, ".eh_frame") == 0) {
+ sCache->mEHFrame = sec;
+ }
+ else if (strcmp(sec->name, ".debug_pubnames") == 0) {
+ pub_names = sec;
+ }
+ else if (strcmp(sec->name, ".debug_pubtypes") == 0) {
+ pub_types = sec;
+ }
+ }
+
+ if (debug_info) {
+ if (pub_names) load_pub_names(debug_info, pub_names, &sCache->mPubNames);
+ if (pub_types) load_pub_names(debug_info, pub_types, &sCache->mPubTypes);
+ }
+
+ if (trap.error) exception(trap.error);
+}
+
+static U2_T gop_gAttr = 0;
+static U2_T gop_gForm = 0;
+static U8_T gop_gFormData = 0;
+static size_t gop_gFormDataSize = 0;
+static void * gop_gFormDataAddr = NULL;
+static ELF_Section * gop_gFormSection = NULL;
+static U8_T gop_gSpecification = 0;
+static U8_T gop_gAbstractOrigin = 0;
+
+static void get_object_property_callback(U2_T Tag, U2_T Attr, U2_T Form) {
+ if (Attr == AT_specification_v2) gop_gSpecification = dio_gFormData;
+ if (Attr == AT_abstract_origin) gop_gAbstractOrigin = dio_gFormData;
+ if (Attr != gop_gAttr) return;
+ gop_gForm = Form;
+ gop_gFormData = dio_gFormData;
+ gop_gFormDataSize = dio_gFormDataSize;
+ gop_gFormDataAddr = dio_gFormDataAddr;
+ gop_gFormSection = dio_gFormSection;
+}
+
+U8_T get_numeric_property_value(PropertyValue * Value) {
+ U8_T Res = 0;
+
+ if (Value->mPieces != NULL || Value->mRegister != NULL) {
+ str_exception(ERR_INV_CONTEXT, "Constant DWARF attribute value expected");
+ }
+ else if (Value->mAddr != NULL) {
+ size_t i;
+ for (i = 0; i < Value->mSize; i++) {
+ Res = (Res << 8) | Value->mAddr[Value->mBigEndian ? i : Value->mSize - i - 1];
+ }
+ }
+ else {
+ Res = Value->mValue;
+ }
+ return Res;
+}
+
+static void read_dwarf_object_property(Context * Ctx, int Frame, ObjectInfo * Obj, U2_T Attr, PropertyValue * Value) {
+
+ memset(Value, 0, sizeof(PropertyValue));
+ Value->mContext = Ctx;
+ Value->mFrame = Frame;
+ Value->mObject = Obj;
+ Value->mAttr = Attr;
+ Value->mBigEndian = Obj->mCompUnit->mFile->big_endian;
+
+ if (Obj->mTag >= TAG_fund_type && Obj->mTag < TAG_fund_type + 0x100) {
+ /* Virtual DWARF object that is created by DWARF reader. It has no properties. */
+ if (Obj->mTag == TAG_fund_type) {
+ if (Attr == AT_byte_size) {
+ Value->mValue = get_fund_type_size(Obj->mCompUnit, Obj->u.mFundType);
+ return;
+ }
+ }
+ else if (Obj->mTag == TAG_index_range) {
+ /* TAG_index_range is virtual DWARF object that is created by DWARF reader. It has no properties. */
+ if (Attr == AT_lower_bound) {
+ switch (Obj->u.mRange.mFmt) {
+ case FMT_FT_C_C:
+ case FMT_FT_C_X:
+ case FMT_UT_C_C:
+ case FMT_UT_C_X:
+ Value->mValue = Obj->u.mRange.mLow.mValue;
+ return;
+ case FMT_FT_X_C:
+ case FMT_FT_X_X:
+ case FMT_UT_X_C:
+ case FMT_UT_X_X:
+ Value->mForm = FORM_BLOCK2;
+ Value->mAddr = Obj->u.mRange.mLow.mExpr.mAddr;
+ Value->mSize = Obj->u.mRange.mLow.mExpr.mSize;
+ return;
+ }
+ }
+ if (Attr == AT_upper_bound) {
+ switch (Obj->u.mRange.mFmt) {
+ case FMT_FT_C_C:
+ case FMT_FT_X_C:
+ case FMT_UT_C_C:
+ case FMT_UT_X_C:
+ Value->mValue = Obj->u.mRange.mHigh.mValue;
+ return;
+ case FMT_FT_C_X:
+ case FMT_FT_X_X:
+ case FMT_UT_C_X:
+ case FMT_UT_X_X:
+ Value->mForm = FORM_BLOCK2;
+ Value->mAddr = Obj->u.mRange.mHigh.mExpr.mAddr;
+ Value->mSize = Obj->u.mRange.mHigh.mExpr.mSize;
+ return;
+ }
+ }
+ }
+ else if (Obj->mTag == TAG_mod_pointer || Obj->mTag == TAG_mod_reference) {
+ if (Attr == AT_byte_size) {
+ Value->mValue = Obj->mCompUnit->mDesc.mAddressSize;
+ return;
+ }
+ }
+ exception(ERR_SYM_NOT_FOUND);
+ }
+
+ sCompUnit = Obj->mCompUnit;
+ sCache = (DWARFCache *)sCompUnit->mFile->dwarf_dt_cache;
+ sDebugSection = sCompUnit->mDesc.mSection;
+ dio_EnterSection(&sCompUnit->mDesc, sDebugSection, Obj->mID - sDebugSection->addr);
+ for (;;) {
+ gop_gAttr = Attr;
+ gop_gForm = 0;
+ gop_gSpecification = 0;
+ gop_gAbstractOrigin = 0;
+ dio_ReadEntry(get_object_property_callback, Attr);
+ dio_ExitSection();
+ if (gop_gForm != 0) break;
+ if (gop_gSpecification != 0) dio_EnterSection(&sCompUnit->mDesc, sDebugSection, gop_gSpecification - sDebugSection->addr);
+ else if (gop_gAbstractOrigin != 0) dio_EnterSection(&sCompUnit->mDesc, sDebugSection, gop_gAbstractOrigin - sDebugSection->addr);
+ else break;
+ }
+
+ switch (Value->mForm = gop_gForm) {
+ case FORM_REF :
+ case FORM_REF_ADDR :
+ case FORM_REF1 :
+ case FORM_REF2 :
+ case FORM_REF4 :
+ case FORM_REF8 :
+ case FORM_REF_UDATA :
+ if (Attr == AT_import) {
+ Value->mValue = gop_gFormData;
+ }
+ else {
+ PropertyValue ValueAddr;
+ ObjectInfo * RefObj = find_object(sCache, gop_gFormData);
+
+ if (RefObj == NULL) exception(ERR_INV_DWARF);
+ read_and_evaluate_dwarf_object_property(Ctx, Frame, 0, RefObj, AT_location, &ValueAddr);
+ if (ValueAddr.mRegister != NULL) {
+ static U1_T Buf[8];
+ StackFrame * Frame = NULL;
+ if (get_frame_info(ValueAddr.mContext, ValueAddr.mFrame, &Frame) < 0) exception(errno);
+ if (read_reg_bytes(Frame, ValueAddr.mRegister, 0, ValueAddr.mRegister->size, Buf) < 0) exception(errno);
+ Value->mAddr = Buf;
+ Value->mSize = ValueAddr.mSize;
+ Value->mBigEndian = ValueAddr.mBigEndian;
+ }
+ else {
+ static U1_T Buf[8];
+ PropertyValue ValueSize;
+ ContextAddress Addr;
+ size_t Size;
+
+ Addr = (ContextAddress)get_numeric_property_value(&ValueAddr);
+ read_and_evaluate_dwarf_object_property(Ctx, Frame, Addr, RefObj, AT_byte_size, &ValueSize);
+ Size = (size_t)get_numeric_property_value(&ValueSize);
+ if (Size < 1 || Size > sizeof(Buf)) exception(ERR_INV_DATA_TYPE);
+ if (context_read_mem(Ctx, Addr, Buf, Size) < 0) exception(errno);
+ Value->mAddr = Buf;
+ Value->mSize = Size;
+ }
+ }
+ break;
+ case FORM_DATA1 :
+ case FORM_DATA2 :
+ case FORM_DATA4 :
+ case FORM_DATA8 :
+ case FORM_FLAG :
+ case FORM_BLOCK1 :
+ case FORM_BLOCK2 :
+ case FORM_BLOCK4 :
+ case FORM_BLOCK :
+ case FORM_STRP :
+ Value->mAddr = (U1_T *)gop_gFormDataAddr;
+ Value->mSize = gop_gFormDataSize;
+ break;
+ case FORM_SDATA :
+ case FORM_UDATA :
+ Value->mValue = gop_gFormData;
+ break;
+ case FORM_ADDR :
+ Value->mValue = elf_map_to_run_time_address(Ctx, Obj->mCompUnit->mFile, gop_gFormSection, (ContextAddress)gop_gFormData);
+ break;
+ default:
+ if (Attr == AT_data_member_location && Obj->mTag == TAG_member && Obj->mParent->mTag == TAG_union_type) {
+ Value->mForm = FORM_UDATA;
+ Value->mValue = 0;
+ break;
+ }
+ if (Attr == AT_byte_size) {
+ if (Obj->mTag == TAG_pointer_type || Obj->mTag == TAG_reference_type || Obj->mTag == TAG_mod_pointer || Obj->mTag == TAG_mod_reference) {
+ Value->mForm = FORM_UDATA;
+ Value->mValue = sCompUnit->mDesc.mAddressSize;
+ break;
+ }
+ if (Obj->mTag == TAG_ptr_to_member_type) {
+ Value->mForm = FORM_UDATA;
+ Value->mValue = sCompUnit->mDesc.mAddressSize * 2;
+ break;
+ }
+ }
+ exception(ERR_SYM_NOT_FOUND);
+ }
+
+ sCompUnit = NULL;
+ sCache = NULL;
+ sDebugSection = NULL;
+}
+
+void read_and_evaluate_dwarf_object_property(Context * Ctx, int Frame, U8_T Base, ObjectInfo * Obj, U2_T Attr, PropertyValue * Value) {
+ read_dwarf_object_property(Ctx, Frame, Obj, Attr, Value);
+ assert(Value->mContext == Ctx);
+ assert(Value->mFrame == Frame);
+ assert(Value->mObject == Obj);
+ assert(Value->mAttr == Attr);
+ if (Attr == AT_location || Attr == AT_data_member_location || Attr == AT_frame_base) {
+ switch (Value->mForm) {
+ case FORM_DATA4 :
+ case FORM_DATA8 :
+ case FORM_BLOCK1 :
+ case FORM_BLOCK2 :
+ case FORM_BLOCK4 :
+ case FORM_BLOCK :
+ dwarf_evaluate_expression(Base, Value);
+ break;
+ }
+ }
+ else if (Attr == AT_count || Attr == AT_byte_size || Attr == AT_lower_bound || Attr == AT_upper_bound) {
+ switch (Value->mForm) {
+ case FORM_BLOCK1 :
+ case FORM_BLOCK2 :
+ case FORM_BLOCK4 :
+ case FORM_BLOCK :
+ dwarf_evaluate_expression(Base, Value);
+ break;
+ }
+ }
+}
+
+static void free_unit_cache(CompUnit * Unit) {
+ Unit->mFilesCnt = 0;
+ Unit->mFilesMax = 0;
+ loc_free(Unit->mFiles);
+ Unit->mFiles = NULL;
+
+ Unit->mDirsCnt = 0;
+ Unit->mDirsMax = 0;
+ loc_free(Unit->mDirs);
+ Unit->mDirs = NULL;
+
+ while (Unit->mStatesCnt > 0) {
+ loc_free(Unit->mStates[--Unit->mStatesCnt].mFileName);
+ }
+ loc_free(Unit->mStates);
+ loc_free(Unit->mStatesIndex);
+ Unit->mStates = NULL;
+ Unit->mStatesMax = 0;
+ Unit->mStatesIndex = NULL;
+}
+
+static void free_dwarf_cache(ELF_File * file) {
+ DWARFCache * Cache = (DWARFCache *)file->dwarf_dt_cache;
+ if (Cache != NULL) {
+ assert(Cache->magic == DWARF_CACHE_MAGIC);
+ Cache->magic = 0;
+ while (Cache->mCompUnits != NULL) {
+ CompUnit * Unit = Cache->mCompUnits->mCompUnit;
+ Cache->mCompUnits = Cache->mCompUnits->mSibling;
+ free_unit_cache(Unit);
+ loc_free(Unit);
+ }
+ while (Cache->mObjectList != NULL) {
+ ObjectArray * Buf = Cache->mObjectList;
+ Cache->mObjectList = Buf->mNext;
+ loc_free(Buf);
+ }
+ loc_free(Cache->mObjectHash);
+ loc_free(Cache->mAddrRanges);
+ loc_free(Cache->mFrameInfoRanges);
+ loc_free(Cache->mPubNames.mHash);
+ loc_free(Cache->mPubNames.mNext);
+ loc_free(Cache->mPubTypes.mHash);
+ loc_free(Cache->mPubTypes.mNext);
+ loc_free(Cache->mFileInfoHash);
+ loc_free(Cache);
+ file->dwarf_dt_cache = NULL;
+ }
+}
+
+DWARFCache * get_dwarf_cache(ELF_File * file) {
+ DWARFCache * Cache = (DWARFCache *)file->dwarf_dt_cache;
+ if (Cache == NULL) {
+ Trap trap;
+ if (!sCloseListenerOK) {
+ elf_add_close_listener(free_dwarf_cache);
+ sCloseListenerOK = 1;
+ }
+ sCache = Cache = (DWARFCache *)(file->dwarf_dt_cache = loc_alloc_zero(sizeof(DWARFCache)));
+ sCache->magic = DWARF_CACHE_MAGIC;
+ sCache->mFile = file;
+ sCache->mObjectArrayPos = OBJECT_ARRAY_SIZE;
+ if (set_trap(&trap)) {
+ dio_LoadAbbrevTable(file);
+ load_debug_sections();
+ load_addr_ranges();
+ clear_trap(&trap);
+ }
+ else {
+ sCache->mErrorReport = get_error_report(trap.error);
+ }
+ sCache = NULL;
+ }
+ if (Cache->mErrorReport) exception(set_error_report_errno(Cache->mErrorReport));
+ return Cache;
+}
+
+static void add_dir(CompUnit * Unit, char * Name) {
+ if (Unit->mDirsCnt >= Unit->mDirsMax) {
+ Unit->mDirsMax = Unit->mDirsMax == 0 ? 16 : Unit->mDirsMax * 2;
+ Unit->mDirs = (char **)loc_realloc(Unit->mDirs, sizeof(char *) * Unit->mDirsMax);
+ }
+ Unit->mDirs[Unit->mDirsCnt++] = Name;
+}
+
+static void add_file(CompUnit * Unit, FileInfo * file) {
+ file->mNameHash = calc_file_name_hash(file->mName);
+ if (Unit->mFilesCnt >= Unit->mFilesMax) {
+ Unit->mFilesMax = Unit->mFilesMax == 0 ? 16 : Unit->mFilesMax * 2;
+ Unit->mFiles = (FileInfo *)loc_realloc(Unit->mFiles, sizeof(FileInfo) * Unit->mFilesMax);
+ }
+ if (file->mDir == NULL) file->mDir = Unit->mDir;
+ Unit->mFiles[Unit->mFilesCnt++] = *file;
+}
+
+static void add_state(CompUnit * Unit, LineNumbersState * state) {
+ if (Unit->mStatesCnt >= Unit->mStatesMax) {
+ Unit->mStatesMax = Unit->mStatesMax == 0 ? 128 : Unit->mStatesMax * 2;
+ Unit->mStates = (LineNumbersState *)loc_realloc(Unit->mStates, sizeof(LineNumbersState) * Unit->mStatesMax);
+ }
+ Unit->mStates[Unit->mStatesCnt++] = *state;
+}
+
+static int state_address_comparator(const void * x1, const void * x2) {
+ LineNumbersState * s1 = (LineNumbersState *)x1;
+ LineNumbersState * s2 = (LineNumbersState *)x2;
+ if (s1->mAddress < s2->mAddress) return -1;
+ if (s1->mAddress > s2->mAddress) return +1;
+ return 0;
+}
+
+static int state_text_pos_comparator(const void * x1, const void * x2) {
+ LineNumbersState * s1 = *(LineNumbersState **)x1;
+ LineNumbersState * s2 = *(LineNumbersState **)x2;
+ if (s1->mFile < s2->mFile) return -1;
+ if (s1->mFile > s2->mFile) return +1;
+ if (s1->mLine < s2->mLine) return -1;
+ if (s1->mLine > s2->mLine) return +1;
+ if (s1->mColumn < s2->mColumn) return -1;
+ if (s1->mColumn > s2->mColumn) return +1;
+ if (s1->mAddress < s2->mAddress) return -1;
+ if (s1->mAddress > s2->mAddress) return +1;
+ return 0;
+}
+
+static void compute_reverse_lookup_indices(DWARFCache * Cache, CompUnit * Unit) {
+ U4_T i;
+ qsort(Unit->mStates, Unit->mStatesCnt, sizeof(LineNumbersState), state_address_comparator);
+ Unit->mStatesIndex = (LineNumbersState **)loc_alloc(sizeof(LineNumbersState *) * Unit->mStatesCnt);
+ for (i = 0; i < Unit->mStatesCnt; i++) Unit->mStatesIndex[i] = Unit->mStates + i;
+ qsort(Unit->mStatesIndex, Unit->mStatesCnt, sizeof(LineNumbersState *), state_text_pos_comparator);
+ for (i = 1; i < Unit->mStatesCnt; i++) {
+ LineNumbersState * s = Unit->mStatesIndex[i - 1];
+ LineNumbersState * n = Unit->mStatesIndex[i];
+ s->mNext = n - Unit->mStates;
+ }
+ if (Cache->mFileInfoHash == NULL) {
+ Cache->mFileInfoHashSize = 251;
+ Cache->mFileInfoHash = (FileInfo **)loc_alloc_zero(sizeof(FileInfo *) * Cache->mFileInfoHashSize);
+ }
+ for (i = 0; i < Unit->mFilesCnt; i++) {
+ FileInfo * File = Unit->mFiles + i;
+ unsigned h = File->mNameHash % Cache->mFileInfoHashSize;
+ File->mCompUnit = Unit;
+ File->mNextInHash = Cache->mFileInfoHash[h];
+ Cache->mFileInfoHash[h] = File;
+ }
+}
+
+static void load_line_numbers_v1(CompUnit * Unit, U4_T unit_size) {
+ LineNumbersState state;
+ ELF_Section * s = NULL;
+ ContextAddress addr = 0;
+ U4_T line = 0;
+
+ memset(&state, 0, sizeof(state));
+ addr = (ContextAddress)dio_ReadAddress(&s);
+ while (dio_GetPos() < Unit->mLineInfoOffs + unit_size) {
+ state.mLine = dio_ReadU4();
+ state.mColumn = dio_ReadU2();
+ if (state.mColumn == 0xffffu) state.mColumn = 0;
+ state.mAddress = addr + dio_ReadU4();
+ if (state.mLine == 0) {
+ state.mLine = line + 1;
+ state.mColumn = 0;
+ }
+ add_state(Unit, &state);
+ line = state.mLine;
+ }
+}
+
+static void load_line_numbers_v2(CompUnit * Unit, U8_T unit_size, int dwarf64) {
+ U8_T header_pos = 0;
+ U1_T opcode_base = 0;
+ U1_T opcode_size[256];
+ U8_T header_size = 0;
+ U1_T min_instruction_length = 0;
+ U1_T is_stmt_default = 0;
+ I1_T line_base = 0;
+ U1_T line_range = 0;
+ LineNumbersState state;
+
+ dio_ReadU2(); /* line info version */
+ header_size = dwarf64 ? dio_ReadU8() : (U8_T)dio_ReadU4();
+ header_pos = dio_GetPos();
+ min_instruction_length = dio_ReadU1();
+ is_stmt_default = dio_ReadU1() != 0;
+ line_base = (I1_T)dio_ReadU1();
+ line_range = dio_ReadU1();
+ opcode_base = dio_ReadU1();
+ memset(opcode_size, 0, sizeof(opcode_size));
+ dio_Read(opcode_size + 1, opcode_base - 1);
+
+ /* Read directory names */
+ for (;;) {
+ char * Name = dio_ReadString();
+ if (Name == NULL) break;
+ add_dir(Unit, Name);
+ }
+
+ /* Read source files info */
+ for (;;) {
+ U4_T dir = 0;
+ FileInfo file;
+ memset(&file, 0, sizeof(file));
+ file.mName = dio_ReadString();
+ if (file.mName == NULL) break;
+ dir = dio_ReadULEB128();
+ if (dir > 0 && dir <= Unit->mDirsCnt) file.mDir = Unit->mDirs[dir - 1];
+ file.mModTime = dio_ReadULEB128();
+ file.mSize = dio_ReadULEB128();
+ add_file(Unit, &file);
+ }
+
+ /* Run the program */
+ if (header_pos + header_size != dio_GetPos())
+ str_exception(ERR_INV_DWARF, "Invalid line info header");
+ memset(&state, 0, sizeof(state));
+ state.mFile = 1;
+ state.mLine = 1;
+ if (is_stmt_default) state.mFlags |= LINE_IsStmt;
+ while (dio_GetPos() < Unit->mLineInfoOffs + unit_size) {
+ U1_T opcode = dio_ReadU1();
+ if (opcode >= opcode_base) {
+ state.mLine += (unsigned)((int)((opcode - opcode_base) % line_range) + line_base);
+ state.mAddress += (opcode - opcode_base) / line_range * min_instruction_length;
+ add_state(Unit, &state);
+ state.mFlags &= ~(LINE_BasicBlock | LINE_PrologueEnd | LINE_EpilogueBegin);
+ }
+ else if (opcode == 0) {
+ U4_T op_size = dio_ReadULEB128();
+ U8_T op_pos = dio_GetPos();
+ switch (dio_ReadU1()) {
+ case DW_LNE_define_file: {
+ U4_T dir = 0;
+ FileInfo file;
+ memset(&file, 0, sizeof(file));
+ file.mName = dio_ReadString();
+ dir = dio_ReadULEB128();
+ if (dir > 0 && dir <= Unit->mDirsCnt) file.mDir = Unit->mDirs[dir - 1];
+ file.mModTime = dio_ReadULEB128();
+ file.mSize = dio_ReadULEB128();
+ add_file(Unit, &file);
+ break;
+ }
+ case DW_LNE_end_sequence:
+ state.mFlags |= LINE_EndSequence;
+ add_state(Unit, &state);
+ memset(&state, 0, sizeof(state));
+ state.mFile = 1;
+ state.mLine = 1;
+ if (is_stmt_default) state.mFlags |= LINE_IsStmt;
+ else state.mFlags &= ~LINE_IsStmt;
+ break;
+ case DW_LNE_set_address:
+ {
+ ELF_Section * s = NULL;
+ state.mAddress = (ContextAddress)dio_ReadAddress(&s);
+ if (s != Unit->mTextSection) state.mAddress = 0;
+ }
+ break;
+ default:
+ dio_Skip(op_size - 1);
+ break;
+ }
+ if (dio_GetPos() != op_pos + op_size)
+ str_exception(ERR_INV_DWARF, "Invalid line info op size");
+ }
+ else {
+ switch (opcode) {
+ case DW_LNS_copy:
+ add_state(Unit, &state);
+ state.mFlags &= ~(LINE_BasicBlock | LINE_PrologueEnd | LINE_EpilogueBegin);
+ break;
+ case DW_LNS_advance_pc:
+ state.mAddress += (ContextAddress)(dio_ReadU8LEB128() * min_instruction_length);
+ break;
+ case DW_LNS_advance_line:
+ state.mLine += dio_ReadSLEB128();
+ break;
+ case DW_LNS_set_file:
+ state.mFile = dio_ReadULEB128();
+ break;
+ case DW_LNS_set_column:
+ state.mColumn = (U2_T)dio_ReadULEB128();
+ break;
+ case DW_LNS_negate_stmt:
+ state.mFlags ^= LINE_IsStmt;
+ break;
+ case DW_LNS_set_basic_block:
+ state.mFlags |= LINE_BasicBlock;
+ break;
+ case DW_LNS_const_add_pc:
+ state.mAddress += (255 - opcode_base) / line_range * min_instruction_length;
+ break;
+ case DW_LNS_fixed_advance_pc:
+ state.mAddress += dio_ReadU2();
+ break;
+ case DW_LNS_set_prologue_end:
+ state.mFlags |= LINE_PrologueEnd;
+ break;
+ case DW_LNS_set_epilogue_begin:
+ state.mFlags |= LINE_EpilogueBegin;
+ break;
+ case DW_LNS_set_isa:
+ state.mISA = (U1_T)dio_ReadULEB128();
+ break;
+ default:
+ str_exception(ERR_INV_DWARF, "Invalid line info op code");
+ break;
+ }
+ }
+ }
+}
+
+void load_line_numbers(CompUnit * Unit) {
+ Trap trap;
+ DWARFCache * Cache = (DWARFCache *)Unit->mFile->dwarf_dt_cache;
+ ELF_Section * LineInfoSection = Unit->mDesc.mVersion <= 1 ? Cache->mDebugLineV1 : Cache->mDebugLine;
+ if (LineInfoSection == NULL) return;
+ if (Unit->mLineInfoLoaded) return;
+ if (elf_load(LineInfoSection)) exception(errno);
+ dio_EnterSection(&Unit->mDesc, LineInfoSection, Unit->mLineInfoOffs);
+ if (set_trap(&trap)) {
+ U8_T unit_size = 0;
+ FileInfo file;
+ memset(&file, 0, sizeof(file));
+ file.mDir = Unit->mDir;
+ file.mName = Unit->mObject->mName;
+ add_file(Unit, &file);
+ /* Read header */
+ unit_size = dio_ReadU4();
+ if (Unit->mDesc.mVersion <= 1) {
+ /* DWARF 1.1 */
+ load_line_numbers_v1(Unit, (U4_T)unit_size);
+ }
+ else {
+ /* DWARF 2+ */
+ int dwarf64 = 0;
+ if (unit_size == 0xffffffffu) {
+ unit_size = dio_ReadU8();
+ unit_size += 12;
+ dwarf64 = 1;
+ }
+ else {
+ unit_size += 4;
+ }
+ load_line_numbers_v2(Unit, unit_size, dwarf64);
+ }
+ dio_ExitSection();
+ compute_reverse_lookup_indices(Cache, Unit);
+ Unit->mLineInfoLoaded = 1;
+ clear_trap(&trap);
+ }
+ else {
+ dio_ExitSection();
+ free_unit_cache(Unit);
+ exception(trap.error);
+ }
+}
+
+UnitAddressRange * find_comp_unit_addr_range(DWARFCache * cache, ContextAddress addr_min, ContextAddress addr_max) {
+ unsigned l = 0;
+ unsigned h = cache->mAddrRangesCnt;
+ while (l < h) {
+ unsigned k = (h + l) / 2;
+ UnitAddressRange * rk = cache->mAddrRanges + k;
+ if (rk->mAddr <= addr_max && rk->mAddr + rk->mSize > addr_min) {
+ int first = 1;
+ if (k > 0) {
+ UnitAddressRange * rp = rk - 1;
+ first = rp->mAddr + rp->mSize <= addr_min;
+ }
+ if (first) return rk;
+ }
+ if (rk->mAddr >= addr_min) h = k;
+ else l = k + 1;
+ }
+ return NULL;
+}
+
+#endif /* ENABLE_ELF && ENABLE_DebugContext */
diff --git a/agent/tcf/services/dwarfcache.h b/agent/tcf/services/dwarfcache.h
new file mode 100644
index 00000000..93bec126
--- /dev/null
+++ b/agent/tcf/services/dwarfcache.h
@@ -0,0 +1,273 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 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
+ *******************************************************************************/
+
+/*
+ * This module implements caching of DWARF debug information.
+ *
+ * Cached data stays in memory at least until end of the current event dispatch cycle.
+ * To lock data for longer period of time clients can use ELF_File.ref_cnt.
+ *
+ * Functions in this module use exceptions to report errors, see exceptions.h
+ */
+#ifndef D_dwarfcache
+#define D_dwarfcache
+
+#include <config.h>
+
+#if ENABLE_ELF && ENABLE_DebugContext
+
+#include <services/tcf_elf.h>
+#include <services/dwarfio.h>
+#include <framework/errors.h>
+
+typedef struct FileInfo FileInfo;
+typedef struct ObjectInfo ObjectInfo;
+typedef struct PubNamesInfo PubNamesInfo;
+typedef struct PubNamesTable PubNamesTable;
+typedef struct ObjectArray ObjectArray;
+typedef struct SymbolInfo SymbolInfo;
+typedef struct PropertyValuePiece PropertyValuePiece;
+typedef struct PropertyValue PropertyValue;
+typedef struct LineNumbersState LineNumbersState;
+typedef struct CompUnit CompUnit;
+typedef struct SymbolSection SymbolSection;
+typedef struct UnitAddressRange UnitAddressRange;
+typedef struct FrameInfoRange FrameInfoRange;
+typedef struct DWARFCache DWARFCache;
+
+struct FileInfo {
+ char * mName;
+ char * mDir;
+ U4_T mModTime;
+ U4_T mSize;
+ unsigned mNameHash;
+ FileInfo * mNextInHash;
+ CompUnit * mCompUnit;
+};
+
+#define TAG_fund_type 0x2000
+#define TAG_index_range 0x2001
+#define TAG_mod_pointer 0x2002
+#define TAG_mod_reference 0x2003
+
+struct ObjectInfo {
+ ObjectInfo * mHashNext;
+ ObjectInfo * mSibling;
+ ObjectInfo * mChildren;
+ ObjectInfo * mParent;
+
+ U8_T mID; /* Link-time debug information entry address: address of .debug_info section + offset in the section */
+ U2_T mTag;
+ CompUnit * mCompUnit;
+ ObjectInfo * mType;
+ char * mName;
+
+ union {
+ U2_T mFundType;
+ struct {
+ ContextAddress mLowPC;
+ ContextAddress mHighPC;
+ } mAddr;
+ struct {
+ U2_T mFmt;
+ union {
+ I8_T mValue;
+ struct {
+ U1_T * mAddr;
+ size_t mSize;
+ } mExpr;
+ } mLow;
+ union {
+ I8_T mValue;
+ struct {
+ U1_T * mAddr;
+ size_t mSize;
+ } mExpr;
+ } mHigh;
+ } mRange;
+ } u;
+};
+
+#define OBJECT_ARRAY_SIZE 128
+
+struct ObjectArray {
+ ObjectArray * mNext;
+ ObjectInfo mArray[OBJECT_ARRAY_SIZE];
+};
+
+struct PubNamesInfo {
+ unsigned mNext;
+ U8_T mID;
+};
+
+struct PubNamesTable {
+ unsigned * mHash;
+ PubNamesInfo * mNext;
+ unsigned mCnt;
+ unsigned mMax;
+};
+
+struct PropertyValuePiece {
+ int mBigEndian;
+ ContextAddress mAddress;
+ RegisterDefinition * mRegister;
+ U4_T mBitOffset;
+ U4_T mBitSize;
+};
+
+struct PropertyValue {
+ Context * mContext;
+ int mFrame;
+ ObjectInfo * mObject;
+ U2_T mAttr;
+ U2_T mForm;
+ U8_T mValue;
+ U1_T * mAddr;
+ size_t mSize;
+ int mBigEndian;
+ RegisterDefinition * mRegister;
+ PropertyValuePiece * mPieces;
+ U4_T mPieceCnt;
+};
+
+#define LINE_IsStmt 0x01
+#define LINE_BasicBlock 0x02
+#define LINE_PrologueEnd 0x04
+#define LINE_EpilogueBegin 0x08
+#define LINE_EndSequence 0x10
+
+struct LineNumbersState {
+ ContextAddress mAddress;
+ char * mFileName;
+ U4_T mNext;
+ U4_T mFile;
+ U4_T mLine;
+ U2_T mColumn;
+ U1_T mFlags;
+ U1_T mISA;
+};
+
+struct CompUnit {
+ ObjectInfo * mObject;
+
+ ELF_File * mFile;
+ ELF_Section * mTextSection;
+
+ U2_T mLanguage;
+ ContextAddress mLowPC;
+ ContextAddress mHighPC;
+
+ DIO_UnitDescriptor mDesc;
+ RegisterIdScope mRegIdScope;
+
+ U8_T mDebugRangesOffs;
+ U8_T mLineInfoOffs;
+ char * mDir;
+
+ U4_T mFilesCnt;
+ U4_T mFilesMax;
+ FileInfo * mFiles;
+
+ U4_T mDirsCnt;
+ U4_T mDirsMax;
+ char ** mDirs;
+
+ U4_T mStatesCnt;
+ U4_T mStatesMax;
+ LineNumbersState * mStates;
+ LineNumbersState ** mStatesIndex;
+ U1_T mLineInfoLoaded;
+
+ CompUnit * mBaseTypes;
+
+ U1_T mARangesFound;
+};
+
+/* Address range of a compilation unit. A unit can occupy multiple address ranges. */
+struct UnitAddressRange {
+ CompUnit * mUnit; /* Compilation unit */
+ ELF_Section * mSection; /* ELF file secdtion that contains the range */
+ ContextAddress mAddr; /* Link-time start address of the range */
+ ContextAddress mSize; /* Size of the range */
+};
+
+struct FrameInfoRange {
+ ContextAddress mAddr;
+ ContextAddress mSize;
+ U8_T mOffset;
+};
+
+#define DWARF_CACHE_MAGIC 0x34625490
+
+struct DWARFCache {
+ int magic;
+ ELF_File * mFile;
+ ErrorReport * mErrorReport;
+ ObjectInfo * mCompUnits;
+ ELF_Section * mDebugLineV1;
+ ELF_Section * mDebugLine;
+ ELF_Section * mDebugLoc;
+ ELF_Section * mDebugRanges;
+ ELF_Section * mDebugFrame;
+ ELF_Section * mEHFrame;
+ ObjectInfo ** mObjectHash;
+ unsigned mObjectHashSize;
+ ObjectArray * mObjectList;
+ unsigned mObjectArrayPos;
+ UnitAddressRange * mAddrRanges;
+ unsigned mAddrRangesCnt;
+ unsigned mAddrRangesMax;
+ PubNamesTable mPubNames;
+ PubNamesTable mPubTypes;
+ FrameInfoRange * mFrameInfoRanges;
+ unsigned mFrameInfoRangesCnt;
+ unsigned mFrameInfoRangesMax;
+ unsigned mFileInfoHashSize;
+ FileInfo ** mFileInfoHash;
+};
+
+/* Return DWARF cache for given file, create and populate the cache if needed, throw an exception if error */
+extern DWARFCache * get_dwarf_cache(ELF_File * file);
+
+/* Return file name hash. The hash is used to search FileInfo. */
+extern unsigned calc_file_name_hash(const char * s);
+
+/* Load line number information for given compilation unit, throw an exception if error */
+extern void load_line_numbers(CompUnit * unit);
+
+/* Find ObjectInfo by ID */
+extern ObjectInfo * find_object(DWARFCache * cache, U8_T ID);
+
+/* Search and return first compilation unit address range in given link-time address range 'addr_min'..'addr_max' (inclusive). */
+extern UnitAddressRange * find_comp_unit_addr_range(DWARFCache * cache, ContextAddress addr_min, ContextAddress addr_max);
+
+/*
+ * Read and evaluate a property of a DWARF object, perform ELF relocations if any.
+ * FORM_ADDR values are mapped to run-time address space.
+ */
+extern void read_and_evaluate_dwarf_object_property(Context * ctx, int frame, U8_T base, ObjectInfo * obj, U2_T attr_tag, PropertyValue * value);
+
+/* Convert PropertyValue to a number */
+extern U8_T get_numeric_property_value(PropertyValue * Value);
+
+/*
+ * Search and return first compilation unit address range in given run-time address range 'addr_min'..'addr_max' (inclusive).
+ * If 'range_rt_addr' not NULL, *range_rt_addr is assigned run-time address of the range.
+ */
+extern struct UnitAddressRange * elf_find_unit(Context * ctx, ContextAddress addr_min, ContextAddress addr_max, ContextAddress * range_rt_addr);
+
+#endif /* ENABLE_ELF && ENABLE_DebugContext */
+
+#endif /* D_dwarfcache */
diff --git a/agent/tcf/services/dwarfexpr.c b/agent/tcf/services/dwarfexpr.c
new file mode 100644
index 00000000..48dee578
--- /dev/null
+++ b/agent/tcf/services/dwarfexpr.c
@@ -0,0 +1,333 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 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
+ *******************************************************************************/
+
+/*
+ * This module implements DWARF expressions evaluation.
+ */
+
+#include <config.h>
+
+#if ENABLE_ELF && ENABLE_DebugContext
+
+#include <assert.h>
+#include <stdio.h>
+#include <framework/events.h>
+#include <framework/myalloc.h>
+#include <framework/exceptions.h>
+#include <framework/errors.h>
+#include <framework/trace.h>
+#include <services/dwarf.h>
+#include <services/dwarfio.h>
+#include <services/dwarfexpr.h>
+#include <services/stacktrace.h>
+#include <services/vm.h>
+
+typedef struct ValuePieces {
+ U4_T mCnt;
+ U4_T mMax;
+ PropertyValuePiece * mArray;
+ struct ValuePieces * mNext;
+} ValuePieces;
+
+static VMState sState;
+static ELF_Section * sSection = NULL;
+static U8_T sSectionOffs = 0;
+static PropertyValue * sValue = NULL;
+static ValuePieces * sValuePieces = NULL;
+
+static ValuePieces * sFreeValuePieces = NULL;
+static ValuePieces * sBusyValuePieces = NULL;
+static unsigned sValuePiecesCnt = 0;
+static int sValuePiecesPosted = 0;
+
+static void free_value_pieces(void * args) {
+ while (sBusyValuePieces != NULL) {
+ ValuePieces * Pieces = sBusyValuePieces;
+ sBusyValuePieces = Pieces->mNext;
+ Pieces->mNext = sFreeValuePieces;
+ sFreeValuePieces = Pieces;
+ }
+ while (sFreeValuePieces != NULL && sValuePiecesCnt > 16) {
+ ValuePieces * Pieces = sFreeValuePieces;
+ sFreeValuePieces = sFreeValuePieces->mNext;
+ sValuePiecesCnt--;
+ loc_free(Pieces->mArray);
+ loc_free(Pieces);
+ }
+}
+
+static ValuePieces * alloc_value_pieces(void) {
+ ValuePieces * Pieces = sFreeValuePieces;
+ if (Pieces == NULL) {
+ Pieces = (ValuePieces *)loc_alloc_zero(sizeof(ValuePieces));
+ sValuePiecesCnt++;
+ }
+ else {
+ sFreeValuePieces = sFreeValuePieces->mNext;
+ }
+ Pieces->mNext = sBusyValuePieces;
+ sBusyValuePieces = Pieces;
+ if (!sValuePiecesPosted && sValuePiecesCnt > 8) {
+ post_event(free_value_pieces, NULL);
+ sValuePiecesPosted = 1;
+ }
+ Pieces->mCnt = 0;
+ return Pieces;
+}
+
+static StackFrame * get_stack_frame(PropertyValue * sValue) {
+ StackFrame * Info = NULL;
+ if (sValue->mFrame == STACK_NO_FRAME) return NULL;
+ if (get_frame_info(sValue->mContext, sValue->mFrame, &Info) < 0) exception(errno);
+ return Info;
+}
+
+static ObjectInfo * get_parent_function(ObjectInfo * Info) {
+ while (Info != NULL) {
+ switch (Info->mTag) {
+ case TAG_global_subroutine:
+ case TAG_subroutine:
+ case TAG_subprogram:
+ case TAG_entry_point:
+ return Info;
+ }
+ Info = Info->mParent;
+ }
+ return NULL;
+}
+
+static U8_T read_address(void) {
+ U8_T addr = 0;
+ ELF_Section * section = NULL;
+ CompUnit * Unit = sValue->mObject->mCompUnit;
+
+ addr = dio_ReadAddress(&section);
+ addr = elf_map_to_run_time_address(sState.ctx, Unit->mFile, section, (ContextAddress)addr);
+ if (addr == 0) str_exception(ERR_INV_ADDRESS, "Object has no RT address");
+ return addr;
+}
+
+static U8_T get_fbreg(void) {
+ PropertyValue FP;
+ CompUnit * Unit = sValue->mObject->mCompUnit;
+ ObjectInfo * Parent = get_parent_function(sValue->mObject);
+ U8_T addr = 0;
+
+ if (Parent == NULL) str_exception(ERR_INV_DWARF, "OP_fbreg: no parent function");
+ memset(&FP, 0, sizeof(FP));
+
+ {
+ PropertyValue * OrgValue = sValue;
+ ValuePieces * OrgValuePieces = sValuePieces;
+ ELF_Section * OrgSection = sSection;
+ U8_T OrgSectionOffs = sSectionOffs;
+ VMState OrgState = sState;
+
+ read_and_evaluate_dwarf_object_property(sState.ctx, sState.stack_frame, 0, Parent, AT_frame_base, &FP);
+
+ assert(sState.ctx == OrgState.ctx);
+ assert(sState.addr_size == OrgState.addr_size);
+ assert(sState.big_endian == OrgState.big_endian);
+
+ sState.code = OrgState.code;
+ sState.code_pos = OrgState.code_pos;
+ sState.code_len = OrgState.code_len;
+ sState.object_address = OrgState.object_address;
+ sSectionOffs = OrgSectionOffs;
+ sSection = OrgSection;
+ sValuePieces = OrgValuePieces;
+ sValue = OrgValue;
+ }
+
+ if (FP.mRegister != NULL) {
+ if (read_reg_value(get_stack_frame(&FP), FP.mRegister, &addr) < 0) exception(errno);
+ }
+ else {
+ addr = get_numeric_property_value(&FP);
+ }
+ dio_EnterSection(&Unit->mDesc, sSection, sSectionOffs + sState.code_pos);
+ return addr + dio_ReadS8LEB128();
+}
+
+static void client_op(uint8_t op) {
+ dio_SetPos(sSectionOffs + sState.code_pos);
+ switch (op) {
+ case OP_addr:
+ sState.stk[sState.stk_pos++] = read_address();
+ break;
+ case OP_fbreg:
+ if (sState.stack_frame == STACK_NO_FRAME) str_exception(ERR_INV_CONTEXT, "Invalid stack frame");
+ sState.stk[sState.stk_pos++] = get_fbreg();
+ break;
+ default:
+ trace(LOG_ALWAYS, "Unsupported DWARF expression op 0x%02x", op);
+ str_exception(ERR_UNSUPPORTED, "Unsupported DWARF expression op");
+ }
+ sState.code_pos = (size_t)(dio_GetPos() - sSectionOffs);
+}
+
+static void evaluate_expression(ELF_Section * Section, U1_T * Buf, size_t Size) {
+ int error = 0;
+ CompUnit * Unit = sValue->mObject->mCompUnit;
+
+ sState.code = Buf;
+ sState.code_len = Size;
+ sState.code_pos = 0;
+ sSection = Section;
+ sSectionOffs = Buf - (U1_T *)Section->data;
+ dio_EnterSection(&Unit->mDesc, sSection, sSectionOffs);
+ if (evaluate_vm_expression(&sState) < 0) error = errno;
+ if (!error && sState.piece_bits) {
+ sValuePieces = alloc_value_pieces();
+ while (!error) {
+ PropertyValuePiece * Piece;
+ if (sValuePieces->mCnt >= sValuePieces->mMax) {
+ sValuePieces->mMax += 8;
+ sValuePieces->mArray = (PropertyValuePiece *)loc_realloc(sValuePieces->mArray,
+ sizeof(PropertyValuePiece) * sValuePieces->mMax);
+ }
+ Piece = sValuePieces->mArray + sValuePieces->mCnt++;
+ memset(Piece, 0, sizeof(PropertyValuePiece));
+ if (sState.reg) {
+ Piece->mRegister = sState.reg;
+ Piece->mBigEndian = sState.reg->big_endian;
+ }
+ else {
+ Piece->mAddress = sState.stk[--sState.stk_pos];
+ Piece->mBigEndian = sState.big_endian;
+ }
+ Piece->mBitOffset = sState.piece_offs;
+ Piece->mBitSize = sState.piece_bits;
+ if (sState.code_pos >= sState.code_len) break;
+ if (evaluate_vm_expression(&sState) < 0) error = errno;
+ }
+ }
+ dio_ExitSection();
+ assert(error || sState.code_pos == sState.code_len);
+ if (error) exception(error);
+}
+
+static void evaluate_location(void) {
+ U8_T IP = 0;
+ U8_T Offset = 0;
+ U8_T Base = 0;
+ CompUnit * Unit = sValue->mObject->mCompUnit;
+ DWARFCache * Cache = (DWARFCache *)Unit->mFile->dwarf_dt_cache;
+ U8_T AddrMax = ~(U8_T)0;
+
+ assert(Cache->magic == DWARF_CACHE_MAGIC);
+ if (Cache->mDebugLoc == NULL) str_exception(ERR_INV_DWARF, "Missing .debug_loc section");
+ dio_EnterSection(&Unit->mDesc, Unit->mDesc.mSection, sValue->mAddr - (U1_T *)Unit->mDesc.mSection->data);
+ Offset = dio_ReadUX(sValue->mSize);
+ dio_ExitSection();
+ Base = Unit->mLowPC;
+ if (Unit->mDesc.mAddressSize < 8) AddrMax = ((U8_T)1 << Unit->mDesc.mAddressSize * 8) - 1;
+ if (read_reg_value(get_stack_frame(sValue), get_PC_definition(sValue->mContext), &IP) < 0) exception(errno);
+ dio_EnterSection(&Unit->mDesc, Cache->mDebugLoc, Offset);
+ for (;;) {
+ ELF_Section * S0 = NULL;
+ ELF_Section * S1 = NULL;
+ U8_T Addr0 = dio_ReadAddress(&S0);
+ U8_T Addr1 = dio_ReadAddress(&S1);
+ if (S0 == NULL) S0 = Unit->mTextSection;
+ if (S1 == NULL) S1 = Unit->mTextSection;
+ if (Addr0 == AddrMax) {
+ Base = Addr1;
+ }
+ else if (Addr0 == 0 && Addr1 == 0) {
+ break;
+ }
+ else if (S0 != S1 || Addr0 > Addr1) {
+ str_exception(ERR_INV_DWARF, "Invalid .debug_loc section");
+ }
+ else {
+ U2_T Size = dio_ReadU2();
+ U8_T RTAddr0 = elf_map_to_run_time_address(sValue->mContext, Unit->mFile, S0, (ContextAddress)(Base + Addr0));
+ U8_T RTAddr1 = Addr1 - Addr0 + RTAddr0;
+ if (RTAddr0 != 0 && IP >= RTAddr0 && IP < RTAddr1) {
+ U1_T * Buf = dio_GetDataPtr();
+ dio_ExitSection();
+ evaluate_expression(Cache->mDebugLoc, Buf, Size);
+ return;
+ }
+ dio_Skip(Size);
+ }
+ }
+ dio_ExitSection();
+ str_exception(ERR_OTHER, "Object is not available at this location in the code");
+}
+
+void dwarf_evaluate_expression(U8_T BaseAddress, PropertyValue * v) {
+ unsigned stk_pos = 0;
+ CompUnit * Unit = v->mObject->mCompUnit;
+
+ sValue = v;
+ sValuePieces = NULL;
+ sState.ctx = sValue->mContext;
+ sState.addr_size = Unit->mDesc.mAddressSize;
+ sState.big_endian = Unit->mFile->big_endian;
+ sState.stack_frame = sValue->mFrame;
+ sState.reg_id_scope = Unit->mRegIdScope;
+ sState.object_address = BaseAddress;
+ sState.client_op = client_op;;
+
+ if (sValue->mAttr != AT_frame_base) sState.stk_pos = 0;
+ stk_pos = sState.stk_pos;
+
+ if (sValue->mAttr == AT_data_member_location) {
+ if (sState.stk_pos >= sState.stk_max) {
+ sState.stk_max += 8;
+ sState.stk = (U8_T *)loc_alloc(sizeof(U8_T) * sState.stk_max);
+ }
+ sState.stk[sState.stk_pos++] = BaseAddress;
+ }
+ if (sValue->mRegister != NULL || sValue->mAddr == NULL || sValue->mSize == 0) {
+ str_exception(ERR_INV_DWARF, "invalid DWARF expression reference");
+ }
+ if (sValue->mForm == FORM_DATA4 || sValue->mForm == FORM_DATA8) {
+ if (sValue->mFrame == STACK_NO_FRAME) str_exception(ERR_INV_CONTEXT, "need stack frame");
+ evaluate_location();
+ }
+ else {
+ evaluate_expression(Unit->mDesc.mSection, sValue->mAddr, sValue->mSize);
+ }
+
+ sValue->mAddr = NULL;
+ sValue->mValue = 0;
+ sValue->mSize = 0;
+ sValue->mBigEndian = sState.big_endian;
+ sValue->mRegister = NULL;
+ sValue->mPieces = NULL;
+ sValue->mPieceCnt = 0;
+
+ if (sValuePieces) {
+ sValue->mPieces = sValuePieces->mArray;
+ sValue->mPieceCnt = sValuePieces->mCnt;
+ }
+ else if (sState.reg) {
+ sValue->mSize = sState.reg->size;
+ sValue->mBigEndian = sState.reg->big_endian;
+ sValue->mRegister = sState.reg;
+ }
+ else {
+ sValue->mValue = sState.stk[--sState.stk_pos];
+ }
+
+ if (sState.stk_pos != stk_pos) {
+ str_exception(ERR_INV_DWARF, "Invalid DWARF expression stack");
+ }
+}
+
+#endif /* ENABLE_ELF && ENABLE_DebugContext */
diff --git a/agent/tcf/services/dwarfexpr.h b/agent/tcf/services/dwarfexpr.h
new file mode 100644
index 00000000..c38f3a00
--- /dev/null
+++ b/agent/tcf/services/dwarfexpr.h
@@ -0,0 +1,34 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 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
+ *******************************************************************************/
+
+/*
+ * This module implements DWARF expressions evaluation.
+ *
+ * Functions in this module use exceptions to report errors, see exceptions.h
+ */
+#ifndef D_dwarfexpr
+#define D_dwarfexpr
+
+#include <config.h>
+
+#if ENABLE_ELF && ENABLE_DebugContext
+
+#include <services/dwarfcache.h>
+
+extern void dwarf_evaluate_expression(U8_T base, PropertyValue * value);
+
+#endif /* ENABLE_ELF && ENABLE_DebugContext */
+
+#endif /* D_dwarfexpr */
diff --git a/agent/tcf/services/dwarfframe.c b/agent/tcf/services/dwarfframe.c
new file mode 100644
index 00000000..843393df
--- /dev/null
+++ b/agent/tcf/services/dwarfframe.c
@@ -0,0 +1,1019 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+
+/*
+ * This module implements handling of .debug_frame and .eh_frame sections.
+ *
+ * Functions in this module use exceptions to report errors, see exceptions.h
+ */
+
+#include <config.h>
+
+#if ENABLE_ELF && ENABLE_DebugContext
+
+#include <assert.h>
+#include <stdio.h>
+#include <framework/exceptions.h>
+#include <framework/myalloc.h>
+#include <framework/trace.h>
+#include <services/dwarf.h>
+#include <services/dwarfio.h>
+#include <services/dwarfframe.h>
+
+#define EH_PE_omit 0xff
+
+#define EH_PE_absptr 0x00
+#define EH_PE_uleb128 0x01
+#define EH_PE_udata2 0x02
+#define EH_PE_udata4 0x03
+#define EH_PE_udata8 0x04
+#define EH_PE_sleb128 0x09
+#define EH_PE_sdata2 0x0a
+#define EH_PE_sdata4 0x0b
+#define EH_PE_sdata8 0x0c
+
+#define EH_PB_pcrel 0x01
+#define EH_PB_textrel 0x02
+#define EH_PB_datarel 0x03
+#define EH_PB_funcrel 0x04
+#define EH_PB_aligned 0x05
+
+#define EH_PE_indirect 0x80
+
+#define RULE_OFFSET 1
+#define RULE_SAME_VALUE 2
+#define RULE_REGISTER 3
+#define RULE_EXPRESSION 4
+#define RULE_VAL_OFFSET 5
+#define RULE_VAL_EXPRESSION 6
+
+typedef struct RegisterRules {
+ int rule;
+ I4_T offset;
+ U8_T expression;
+} RegisterRules;
+
+typedef struct StackFrameRegisters {
+ RegisterRules * regs;
+ int regs_cnt;
+ int regs_max;
+} StackFrameRegisters;
+
+typedef struct StackFrameRules {
+ Context * ctx;
+ ELF_Section * section;
+ RegisterIdScope reg_id_scope;
+ int eh_frame;
+ U1_T version;
+ U1_T address_size;
+ U1_T segment_size;
+ U4_T code_alignment;
+ I4_T data_alignment;
+ U8_T cie_pos;
+ char * cie_aug;
+ U8_T cie_eh_data;
+ ELF_Section * cie_eh_data_section;
+ U4_T fde_aug_length;
+ U1_T * fde_aug_data;
+ U1_T lsda_encoding;
+ U1_T prh_encoding;
+ U1_T addr_encoding;
+ U8_T location;
+ int return_address_register;
+ int cfa_rule;
+ I4_T cfa_offset;
+ U4_T cfa_register;
+ U8_T cfa_expression;
+} StackFrameRules;
+
+static StackFrameRegisters frame_regs;
+static StackFrameRegisters cie_regs;
+static StackFrameRegisters * regs_stack = NULL;
+static int regs_stack_max = 0;
+static int regs_stack_pos = 0;
+
+static StackFrameRules rules;
+
+U8_T dwarf_stack_trace_addr = 0;
+U8_T dwarf_stack_trace_size = 0;
+
+StackTracingCommandSequence * dwarf_stack_trace_fp = NULL;
+
+int dwarf_stack_trace_regs_cnt = 0;
+StackTracingCommandSequence ** dwarf_stack_trace_regs = NULL;
+
+static int trace_regs_max = 0;
+static int trace_cmds_max = 0;
+static int trace_cmds_cnt = 0;
+static StackTracingCommand * trace_cmds = NULL;
+
+static RegisterRules * get_reg(StackFrameRegisters * regs, int reg) {
+ while (reg >= regs->regs_max) {
+ regs->regs_max = regs->regs_max == 0 ? 32 : regs->regs_max * 2;
+ regs->regs = (RegisterRules *)loc_realloc(regs->regs, sizeof(RegisterRules) * regs->regs_max);
+ }
+ while (regs->regs_cnt <= reg) {
+ int n = regs->regs_cnt++;
+ memset(regs->regs + n, 0, sizeof(RegisterRules));
+ /* Architecture specific implied rules */
+ switch (rules.reg_id_scope.machine) {
+ case EM_386:
+ switch (n) {
+ case 4: /* SP */
+ regs->regs[n].rule = RULE_VAL_OFFSET;
+ break;
+ case 3: /* BX */
+ case 5: /* BP */
+ case 6: /* SI */
+ case 7: /* DI */
+ regs->regs[n].rule = RULE_SAME_VALUE;
+ break;
+ }
+ break;
+ case EM_X86_64:
+ switch (n) {
+ case 3: /* BX */
+ case 6: /* BP */
+ case 12: /* R12 */
+ case 13: /* R13 */
+ case 14: /* R14 */
+ case 15: /* R15 */
+ regs->regs[n].rule = RULE_SAME_VALUE;
+ break;
+ case 7: /* SP */
+ regs->regs[n].rule = RULE_VAL_OFFSET;
+ break;
+ }
+ break;
+ case EM_PPC:
+ if (n == 1) {
+ regs->regs[n].rule = RULE_VAL_OFFSET;
+ }
+ else if ((n >= 14 && n <= 31) || (n >= 46 && n <= 63)) {
+ regs->regs[n].rule = RULE_SAME_VALUE;
+ }
+ else if (n == rules.return_address_register) {
+ regs->regs[n].rule = RULE_REGISTER;
+ regs->regs[n].offset = 108;
+ }
+ break;
+ case EM_ARM:
+ if (n >= 4 && n <= 11) { /* Local variables */
+ regs->regs[n].rule = RULE_SAME_VALUE;
+ }
+ else if (n == 13) { /* Stack pointer */
+ regs->regs[n].rule = RULE_VAL_OFFSET;
+ }
+ break;
+ }
+ }
+ return regs->regs + reg;
+}
+
+static void copy_register_rules(StackFrameRegisters * dst, StackFrameRegisters * src) {
+ int n;
+ dst->regs_cnt = 0;
+ for (n = 0; n < src->regs_cnt; n++) {
+ *get_reg(dst, n) = *get_reg(src, n);
+ }
+}
+
+static StackFrameRegisters * get_regs_stack_item(int n) {
+ while (n >= regs_stack_max) {
+ int max = regs_stack_max;
+ regs_stack_max = regs_stack_max == 0 ? 8 : regs_stack_max * 2;
+ regs_stack = (StackFrameRegisters *)loc_realloc(regs_stack, sizeof(StackFrameRegisters) * regs_stack_max);
+ memset(regs_stack + max, 0, sizeof(StackFrameRegisters) * (regs_stack_max - max));
+ }
+ return regs_stack + n;
+}
+
+static U8_T read_frame_data_pointer(U1_T encoding, ELF_Section ** sec) {
+ U8_T v = 0;
+ if (encoding != EH_PE_omit) {
+ U8_T pos = dio_GetPos();
+ switch (encoding & 0xf) {
+ case EH_PE_absptr:
+ v = dio_ReadAddress(sec);
+ break;
+ case EH_PE_uleb128:
+ v = dio_ReadU8LEB128();
+ break;
+ case EH_PE_udata2:
+ v = dio_ReadU2();
+ break;
+ case EH_PE_udata4:
+ v = dio_ReadU4();
+ break;
+ case EH_PE_udata8:
+ v = dio_ReadU8();
+ break;
+ case EH_PE_sleb128:
+ v = dio_ReadS8LEB128();
+ break;
+ case EH_PE_sdata2:
+ v = (I2_T)dio_ReadU2();
+ break;
+ case EH_PE_sdata4:
+ v = (I4_T)dio_ReadU4();
+ break;
+ case EH_PE_sdata8:
+ v = (I8_T)dio_ReadU8();
+ break;
+ default:
+ str_exception(ERR_INV_DWARF, "Unknown encoding of .eh_frame section pointers");
+ break;
+ }
+ if (v != 0 && sec != NULL) {
+ switch ((encoding >> 4) & 0x7) {
+ case 0:
+ break;
+ case EH_PB_pcrel:
+ *sec = rules.section;
+ v += rules.section->addr + pos;
+ break;
+ case EH_PB_datarel:
+ *sec = rules.section;
+ v += rules.section->addr;
+ break;
+ case EH_PB_textrel:
+ case EH_PB_funcrel:
+ case EH_PB_aligned:
+ default:
+ str_exception(ERR_INV_DWARF, "Unknown encoding of .eh_frame section pointers");
+ break;
+ }
+ if (encoding & EH_PE_indirect) {
+ unsigned idx;
+ ELF_File * file = rules.section->file;
+ size_t size = rules.address_size;
+ U8_T res = 0;
+ for (idx = 1; idx < file->section_cnt; idx++) {
+ ELF_Section * sec = file->sections + idx;
+ if ((sec->flags & SHF_ALLOC) == 0) continue;
+ if (sec->addr <= v && sec->addr + sec->size >= v + size) {
+ U1_T * p;
+ size_t i;
+ if (sec->data == NULL && elf_load(sec) < 0) exception(errno);
+ p = (U1_T *)sec->data + (uintptr_t)(v - sec->addr);
+ for (i = 0; i < size; i++) {
+ res = (res << 8) | p[file->big_endian ? i : size - i - 1];
+ }
+ break;
+ }
+ }
+ v = res;
+ }
+ }
+ }
+ return v;
+}
+
+static void exec_stack_frame_instruction(void) {
+ RegisterRules * reg;
+ U4_T n;
+ U1_T op = dio_ReadU1();
+ switch (op) {
+ case 0x00: /* DW_CFA_nop */
+ break;
+ case 0x01: /* DW_CFA_set_loc */
+ rules.location = read_frame_data_pointer(rules.addr_encoding, 0);
+ break;
+ case 0x02: /* DW_CFA_advance_loc1 */
+ rules.location += dio_ReadU1() * rules.code_alignment;
+ break;
+ case 0x03: /* DW_CFA_advance_loc2 */
+ rules.location += dio_ReadU2() * rules.code_alignment;
+ break;
+ case 0x04: /* DW_CFA_advance_loc4 */
+ rules.location += dio_ReadU4() * rules.code_alignment;
+ break;
+ case 0x05: /* DW_CFA_offset_extended */
+ reg = get_reg(&frame_regs, dio_ReadULEB128());
+ reg->rule = RULE_OFFSET;
+ reg->offset = dio_ReadULEB128() * rules.data_alignment;
+ break;
+ case 0x06: /* DW_CFA_restore_extended */
+ n = dio_ReadULEB128();
+ reg = get_reg(&frame_regs, n);
+ *reg = *get_reg(&cie_regs, n);
+ break;
+ case 0x07: /* DW_CFA_undefined */
+ reg = get_reg(&frame_regs, dio_ReadULEB128());
+ memset(reg, 0, sizeof(*reg));
+ break;
+ case 0x08: /* DW_CFA_same_value */
+ reg = get_reg(&frame_regs, dio_ReadULEB128());
+ reg->rule = RULE_SAME_VALUE;
+ break;
+ case 0x09: /* DW_CFA_register */
+ reg = get_reg(&frame_regs, dio_ReadULEB128());
+ reg->rule = RULE_REGISTER;
+ reg->offset = dio_ReadULEB128();
+ break;
+ case 0x0a: /* DW_CFA_remember_state */
+ copy_register_rules(get_regs_stack_item(regs_stack_pos++), &frame_regs);
+ break;
+ case 0x0b: /* DW_CFA_restore_state */
+ if (regs_stack_pos <= 0) {
+ str_exception(ERR_INV_DWARF, "Invalid DW_CFA_restore_state instruction");
+ }
+ copy_register_rules(&frame_regs, get_regs_stack_item(--regs_stack_pos));
+ break;
+ case 0x0c: /* DW_CFA_def_cfa */
+ rules.cfa_rule = RULE_OFFSET;
+ rules.cfa_register = dio_ReadULEB128();
+ rules.cfa_offset = dio_ReadULEB128();
+ break;
+ case 0x0d: /* DW_CFA_def_cfa_register */
+ rules.cfa_rule = RULE_OFFSET;
+ rules.cfa_register = dio_ReadULEB128();
+ break;
+ case 0x0e: /* DW_CFA_def_cfa_offset */
+ rules.cfa_rule = RULE_OFFSET;
+ rules.cfa_offset = dio_ReadULEB128();
+ break;
+ case 0x0f: /* DW_CFA_def_cfa_expression */
+ rules.cfa_rule = RULE_EXPRESSION;
+ rules.cfa_offset = dio_ReadULEB128();
+ rules.cfa_expression = dio_GetPos();
+ dio_Skip(rules.cfa_offset);
+ break;
+ case 0x10: /* DW_CFA_expression */
+ reg = get_reg(&frame_regs, dio_ReadULEB128());
+ reg->rule = RULE_EXPRESSION;
+ reg->offset = dio_ReadULEB128();
+ reg->expression = dio_GetPos();
+ dio_Skip(reg->offset);
+ break;
+ case 0x11: /* DW_CFA_offset_extended_sf */
+ reg = get_reg(&frame_regs, dio_ReadULEB128());
+ reg->rule = RULE_OFFSET;
+ reg->offset = dio_ReadSLEB128() * rules.data_alignment;
+ break;
+ case 0x12: /* DW_CFA_def_cfa_sf */
+ rules.cfa_rule = RULE_OFFSET;
+ rules.cfa_register = dio_ReadULEB128();
+ rules.cfa_offset = dio_ReadSLEB128() * rules.data_alignment;
+ break;
+ case 0x13: /* DW_CFA_def_cfa_offset_sf */
+ rules.cfa_rule = RULE_OFFSET;
+ rules.cfa_offset = dio_ReadSLEB128() * rules.data_alignment;
+ break;
+ case 0x14: /* DW_CFA_val_offset */
+ reg = get_reg(&frame_regs, dio_ReadULEB128());
+ reg->rule = RULE_VAL_OFFSET;
+ reg->offset = dio_ReadULEB128() * rules.data_alignment;
+ break;
+ case 0x15: /* DW_CFA_val_offset_sf */
+ reg = get_reg(&frame_regs, dio_ReadULEB128());
+ reg->rule = RULE_VAL_OFFSET;
+ reg->offset = dio_ReadSLEB128() * rules.data_alignment;
+ break;
+ case 0x16: /* DW_CFA_val_expression */
+ reg = get_reg(&frame_regs, dio_ReadULEB128());
+ reg->rule = RULE_VAL_EXPRESSION;
+ reg->offset = dio_ReadULEB128();
+ reg->expression = dio_GetPos();
+ dio_Skip(reg->offset);
+ break;
+ case 0x2e: /* DW_CFA_GNU_args_size */
+ /* This instruction specifies the total size of the arguments
+ * which have been pushed onto the stack. Not used by the debugger. */
+ dio_ReadULEB128();
+ break;
+ case 0x2f: /* DW_CFA_GNU_negative_offset_extended */
+ /* This instruction is identical to DW_CFA_offset_extended_sf
+ * except that the operand is subtracted to produce the offset. */
+ reg = get_reg(&frame_regs, dio_ReadULEB128());
+ reg->rule = RULE_OFFSET;
+ reg->offset = -dio_ReadSLEB128() * rules.data_alignment;
+ break;
+ default:
+ switch (op >> 6) {
+ case 0:
+ str_exception(ERR_INV_DWARF, "Unsupported instruction in Call Frame Information");
+ break;
+ case 1: /* DW_CFA_advance_loc */
+ rules.location += (op & 0x3f) * rules.code_alignment;
+ break;
+ case 2: /* DW_CFA_offset */
+ reg = get_reg(&frame_regs, op & 0x3f);
+ reg->rule = RULE_OFFSET;
+ reg->offset = dio_ReadULEB128() * rules.data_alignment;
+ break;
+ case 3: /* DW_CFA_restore */
+ n = op & 0x3f;
+ reg = get_reg(&frame_regs, n);
+ *reg = *get_reg(&cie_regs, n);
+ break;
+ }
+ }
+}
+
+static StackTracingCommand * add_command(int op) {
+ StackTracingCommand * cmd = NULL;
+ if (trace_cmds_cnt >= trace_cmds_max) {
+ trace_cmds_max += 16;
+ trace_cmds = (StackTracingCommand *)loc_realloc(trace_cmds, trace_cmds_max * sizeof(StackTracingCommand));
+ }
+ cmd = trace_cmds + trace_cmds_cnt++;
+ memset(cmd, 0, sizeof(*cmd));
+ cmd->cmd = op;
+ return cmd;
+}
+
+static void add_command_sequence(StackTracingCommandSequence ** ptr, RegisterDefinition * reg) {
+ StackTracingCommandSequence * seq = *ptr;
+ if (seq == NULL || seq->cmds_max < trace_cmds_cnt) {
+ *ptr = seq = (StackTracingCommandSequence *)loc_realloc(seq, sizeof(StackTracingCommandSequence) + (trace_cmds_cnt - 1) * sizeof(StackTracingCommand));
+ seq->cmds_max = trace_cmds_cnt;
+ }
+ seq->reg = reg;
+ seq->cmds_cnt = trace_cmds_cnt;
+ memcpy(seq->cmds, trace_cmds, trace_cmds_cnt * sizeof(StackTracingCommand));
+}
+
+static void add_dwarf_expression_commands(U8_T cmds_offs, U4_T cmds_size) {
+ dio_EnterSection(NULL, rules.section, cmds_offs);
+ while (dio_GetPos() < cmds_offs + cmds_size) {
+ U1_T op = dio_ReadU1();
+
+ switch (op) {
+ case OP_addr:
+ {
+ ELF_Section * section = NULL;
+ U8_T lt_addr = dio_ReadAddress(&section);
+ ContextAddress rt_addr = elf_map_to_run_time_address(
+ rules.ctx, rules.section->file, section, (ContextAddress)lt_addr);
+ if (rt_addr == 0) str_exception(ERR_INV_DWARF, "object has no RT address");
+ add_command(SFT_CMD_NUMBER)->num = rt_addr;
+ }
+ break;
+ case OP_deref:
+ {
+ StackTracingCommand * cmd = add_command(SFT_CMD_DEREF);
+ cmd->size = rules.address_size;
+ cmd->big_endian = rules.section->file->big_endian;
+ }
+ break;
+ case OP_deref_size:
+ {
+ StackTracingCommand * cmd = add_command(SFT_CMD_DEREF);
+ cmd->size = dio_ReadU1();
+ cmd->big_endian = rules.section->file->big_endian;
+ }
+ break;
+ case OP_const1u:
+ add_command(SFT_CMD_NUMBER)->num = dio_ReadU1();
+ break;
+ case OP_const1s:
+ add_command(SFT_CMD_NUMBER)->num = (I1_T)dio_ReadU1();
+ break;
+ case OP_const2u:
+ add_command(SFT_CMD_NUMBER)->num = dio_ReadU2();
+ break;
+ case OP_const2s:
+ add_command(SFT_CMD_NUMBER)->num = (I2_T)dio_ReadU2();
+ break;
+ case OP_const4u:
+ add_command(SFT_CMD_NUMBER)->num = dio_ReadU4();
+ break;
+ case OP_const4s:
+ add_command(SFT_CMD_NUMBER)->num = (I4_T)dio_ReadU4();
+ break;
+ case OP_const8u:
+ add_command(SFT_CMD_NUMBER)->num = dio_ReadU8();
+ break;
+ case OP_const8s:
+ add_command(SFT_CMD_NUMBER)->num = (I8_T)dio_ReadU8();
+ break;
+ case OP_constu:
+ add_command(SFT_CMD_NUMBER)->num = dio_ReadU8LEB128();
+ break;
+ case OP_consts:
+ add_command(SFT_CMD_NUMBER)->num = dio_ReadS8LEB128();
+ break;
+ case OP_and:
+ add_command(SFT_CMD_AND);
+ break;
+ case OP_minus:
+ add_command(SFT_CMD_SUB);
+ break;
+ case OP_or:
+ add_command(SFT_CMD_OR);
+ break;
+ case OP_plus:
+ add_command(SFT_CMD_ADD);
+ break;
+ case OP_plus_uconst:
+ add_command(SFT_CMD_NUMBER)->num = dio_ReadU8LEB128();
+ add_command(SFT_CMD_ADD);
+ break;
+ case OP_lit0:
+ case OP_lit1:
+ case OP_lit2:
+ case OP_lit3:
+ case OP_lit4:
+ case OP_lit5:
+ case OP_lit6:
+ case OP_lit7:
+ case OP_lit8:
+ case OP_lit9:
+ case OP_lit10:
+ case OP_lit11:
+ case OP_lit12:
+ case OP_lit13:
+ case OP_lit14:
+ case OP_lit15:
+ case OP_lit16:
+ case OP_lit17:
+ case OP_lit18:
+ case OP_lit19:
+ case OP_lit20:
+ case OP_lit21:
+ case OP_lit22:
+ case OP_lit23:
+ case OP_lit24:
+ case OP_lit25:
+ case OP_lit26:
+ case OP_lit27:
+ case OP_lit28:
+ case OP_lit29:
+ case OP_lit30:
+ case OP_lit31:
+ add_command(SFT_CMD_NUMBER)->num = op - OP_lit0;
+ break;
+ case OP_breg0:
+ case OP_breg1:
+ case OP_breg2:
+ case OP_breg3:
+ case OP_breg4:
+ case OP_breg5:
+ case OP_breg6:
+ case OP_breg7:
+ case OP_breg8:
+ case OP_breg9:
+ case OP_breg10:
+ case OP_breg11:
+ case OP_breg12:
+ case OP_breg13:
+ case OP_breg14:
+ case OP_breg15:
+ case OP_breg16:
+ case OP_breg17:
+ case OP_breg18:
+ case OP_breg19:
+ case OP_breg20:
+ case OP_breg21:
+ case OP_breg22:
+ case OP_breg23:
+ case OP_breg24:
+ case OP_breg25:
+ case OP_breg26:
+ case OP_breg27:
+ case OP_breg28:
+ case OP_breg29:
+ case OP_breg30:
+ case OP_breg31:
+ {
+ I8_T offs = dio_ReadS8LEB128();
+ RegisterDefinition * def = get_reg_by_id(rules.ctx, op - OP_breg0, &rules.reg_id_scope);
+ if (def == NULL) str_exception(errno, "Cannot read DWARF frame info");
+ add_command(SFT_CMD_REGISTER)->reg = def;
+ if (offs != 0) {
+ add_command(SFT_CMD_NUMBER)->num = offs;
+ add_command(SFT_CMD_ADD);
+ }
+ }
+ break;
+ case OP_nop:
+ break;
+ default:
+ trace(LOG_ALWAYS, "Unsupported DWARF expression op 0x%02x", op);
+ str_exception(ERR_UNSUPPORTED, "Unsupported DWARF expression op");
+ }
+ }
+}
+
+static void generate_register_commands(RegisterRules * reg, RegisterDefinition * dst_reg_def, RegisterDefinition * src_reg_def) {
+ if (dst_reg_def == NULL) return;
+ trace_cmds_cnt = 0;
+ switch (reg->rule) {
+ case RULE_VAL_OFFSET:
+ case RULE_OFFSET:
+ add_command(SFT_CMD_FP);
+ if (reg->offset != 0) {
+ add_command(SFT_CMD_NUMBER)->num = reg->offset;
+ add_command(SFT_CMD_ADD);
+ }
+ if (reg->rule == RULE_OFFSET) {
+ StackTracingCommand * cmd = add_command(SFT_CMD_DEREF);
+ cmd->size = dst_reg_def->size;
+ if (cmd->size > rules.address_size) cmd->size = rules.address_size;
+ cmd->big_endian = rules.section->file->big_endian;
+ }
+ break;
+ case RULE_SAME_VALUE:
+ if (src_reg_def == NULL) return;
+ add_command(SFT_CMD_REGISTER)->reg = src_reg_def;
+ break;
+ case RULE_REGISTER:
+ {
+ RegisterDefinition * src_sef = get_reg_by_id(rules.ctx, reg->offset, &rules.reg_id_scope);
+ if (src_sef != NULL) add_command(SFT_CMD_REGISTER)->reg = src_sef;
+ }
+ break;
+ case RULE_EXPRESSION:
+ case RULE_VAL_EXPRESSION:
+ add_command(SFT_CMD_FP);
+ add_dwarf_expression_commands(reg->expression, reg->offset);
+ if (reg->rule == RULE_EXPRESSION) {
+ StackTracingCommand * cmd = add_command(SFT_CMD_DEREF);
+ cmd->size = dst_reg_def->size;
+ if (cmd->size > rules.address_size) cmd->size = rules.address_size;
+ cmd->big_endian = rules.section->file->big_endian;
+ }
+ break;
+ default:
+ str_exception(ERR_INV_DWARF, "Invalid .debug_frame");
+ break;
+ }
+ if (dwarf_stack_trace_regs_cnt >= trace_regs_max) {
+ int i;
+ trace_regs_max += 16;
+ dwarf_stack_trace_regs = (StackTracingCommandSequence **)loc_realloc(dwarf_stack_trace_regs, trace_regs_max * sizeof(StackTracingCommandSequence *));
+ for (i = dwarf_stack_trace_regs_cnt; i < trace_regs_max; i++) dwarf_stack_trace_regs[i] = NULL;
+ }
+ if (trace_cmds_cnt == 0) return;
+ add_command_sequence(dwarf_stack_trace_regs + dwarf_stack_trace_regs_cnt++, dst_reg_def);
+}
+
+static void generate_commands(void) {
+ int i;
+ RegisterRules * reg;
+ RegisterDefinition * reg_def;
+
+ reg = get_reg(&frame_regs, rules.return_address_register);
+ if (reg->rule != 0) {
+ reg_def = get_reg_by_id(rules.ctx, rules.return_address_register, &rules.reg_id_scope);
+ generate_register_commands(reg, get_PC_definition(rules.ctx), reg_def);
+ }
+ for (i = 0; i < frame_regs.regs_cnt; i++) {
+ if (i == rules.return_address_register) continue;
+ reg = get_reg(&frame_regs, i);
+ if (reg->rule == 0) continue;
+ reg_def = get_reg_by_id(rules.ctx, i, &rules.reg_id_scope);
+ generate_register_commands(reg, reg_def, reg_def);
+ }
+
+ trace_cmds_cnt = 0;
+ switch (rules.cfa_rule) {
+ case RULE_OFFSET:
+ reg_def = get_reg_by_id(rules.ctx, rules.cfa_register, &rules.reg_id_scope);
+ if (reg_def != NULL) {
+ add_command(SFT_CMD_REGISTER)->reg = reg_def;
+ if (rules.cfa_offset != 0) {
+ add_command(SFT_CMD_NUMBER)->num = rules.cfa_offset;
+ add_command(SFT_CMD_ADD);
+ }
+ }
+ break;
+ case RULE_EXPRESSION:
+ add_dwarf_expression_commands(rules.cfa_expression, rules.cfa_offset);
+ break;
+ default:
+ str_exception(ERR_INV_DWARF, "Invalid .debug_frame");
+ break;
+ }
+ add_command_sequence(&dwarf_stack_trace_fp, NULL);
+}
+
+static int generate_plt_section_commands(U8_T offs) {
+ RegisterRules * reg = NULL;
+
+ cie_regs.regs_cnt = 0;
+ frame_regs.regs_cnt = 0;
+ switch (rules.reg_id_scope.machine) {
+ case EM_386:
+ rules.cfa_rule = RULE_OFFSET;
+ rules.cfa_register = 4; /* esp */
+ if (offs == 0) {
+ rules.cfa_offset = 8;
+ }
+ else if (offs < 16) {
+ rules.cfa_offset = 12;
+ }
+ else if ((offs - 16) % 16 < 11) {
+ rules.cfa_offset = 4;
+ }
+ else {
+ rules.cfa_offset = 8;
+ }
+ rules.return_address_register = 8; /* eip */
+ reg = get_reg(&frame_regs, rules.return_address_register);
+ reg->rule = RULE_OFFSET;
+ reg->offset = -4;
+ generate_commands();
+ return 1;
+ case EM_X86_64:
+ rules.cfa_rule = RULE_OFFSET;
+ rules.cfa_register = 7; /* rsp */
+ if (offs == 0) {
+ rules.cfa_offset = 16;
+ }
+ else if (offs < 16) {
+ rules.cfa_offset = 24;
+ }
+ else if ((offs - 16) % 16 < 11) {
+ rules.cfa_offset = 8;
+ }
+ else {
+ rules.cfa_offset = 16;
+ }
+ rules.return_address_register = 16; /* rip */
+ reg = get_reg(&frame_regs, rules.return_address_register);
+ reg->rule = RULE_OFFSET;
+ reg->offset = -8;
+ generate_commands();
+ return 1;
+ case EM_PPC:
+ rules.return_address_register = 108; /* LR */
+ rules.cfa_rule = RULE_OFFSET;
+ rules.cfa_register = 1; /* R1 */
+ rules.cfa_offset = 0;
+ generate_commands();
+ return 1;
+ }
+ return 0;
+}
+
+static void read_frame_cie(U8_T fde_pos, U8_T pos) {
+ int cie_dwarf64 = 0;
+ U8_T saved_pos = dio_GetPos();
+ U8_T cie_length = 0;
+ U8_T cie_end = 0;
+
+ rules.cie_pos = pos;
+ if (pos >= rules.section->size) {
+ char msg[256];
+ snprintf(msg, sizeof(msg),
+ "Invalid CIE pointer 0x%" PRIX64
+ " in FDE at 0x%" PRIX64, pos, fde_pos);
+ str_exception(ERR_INV_DWARF, msg);
+ }
+ dio_SetPos(pos);
+ cie_length = dio_ReadU4();
+ if (cie_length == ~(U4_T)0) {
+ cie_length = dio_ReadU8();
+ cie_dwarf64 = 1;
+ }
+ cie_end = dio_GetPos() + cie_length;
+ dio_Skip(cie_dwarf64 ? 8 : 4);
+ rules.version = dio_ReadU1();
+ if (rules.version != 1 && rules.version != 3 && rules.version != 4) {
+ str_exception(ERR_INV_DWARF, "Unsupported version of Call Frame Information");
+ }
+ rules.cie_aug = dio_ReadString();
+ if (rules.cie_aug != NULL && strcmp(rules.cie_aug, "eh") == 0) {
+ rules.cie_eh_data = dio_ReadAddress(&rules.cie_eh_data_section);
+ }
+ if (rules.version >= 4) {
+ rules.address_size = dio_ReadU1();
+ rules.segment_size = dio_ReadU1();
+ }
+ else {
+ rules.address_size = rules.section->file->elf64 ? 8 : 4;
+ rules.segment_size = 0;
+ }
+ if (rules.segment_size != 0) {
+ str_exception(ERR_INV_DWARF, "Unsupported Call Frame Information: segment size != 0");
+ }
+ rules.code_alignment = dio_ReadULEB128();
+ rules.data_alignment = dio_ReadSLEB128();
+ rules.return_address_register = dio_ReadULEB128();
+ rules.lsda_encoding = 0;
+ rules.prh_encoding = 0;
+ rules.addr_encoding = 0;
+ if (rules.cie_aug != NULL && rules.cie_aug[0] == 'z') {
+ U4_T aug_length = dio_ReadULEB128();
+ U8_T aug_pos = dio_GetPos();
+ char * p = rules.cie_aug + 1;
+ while (*p) {
+ switch (*p++) {
+ case 'L':
+ rules.lsda_encoding = dio_ReadU1();
+ break;
+ case 'P':
+ rules.prh_encoding = dio_ReadU1();
+ read_frame_data_pointer(rules.prh_encoding, 0);
+ break;
+ case 'R':
+ rules.addr_encoding = dio_ReadU1();
+ break;
+ }
+ }
+ dio_SetPos(aug_pos + aug_length);
+ }
+ cie_regs.regs_cnt = 0;
+ frame_regs.regs_cnt = 0;
+ regs_stack_pos = 0;
+ while (dio_GetPos() < cie_end) {
+ exec_stack_frame_instruction();
+ }
+ copy_register_rules(&cie_regs, &frame_regs);
+ dio_SetPos(saved_pos);
+}
+
+static void read_frame_fde(ELF_Section * section, U8_T IP, U8_T fde_pos) {
+ int fde_dwarf64 = 0;
+ U8_T fde_length = 0;
+ U8_T fde_end = 0;
+ U8_T ref_pos = 0;
+ U8_T cie_ref = 0;
+ int fde_flag = 0;
+
+ dio_EnterSection(NULL, section, fde_pos);
+ fde_length = dio_ReadU4();
+ assert(fde_length > 0);
+ if (fde_length == ~(U4_T)0) {
+ fde_length = dio_ReadU8();
+ fde_dwarf64 = 1;
+ }
+ ref_pos = dio_GetPos();
+ fde_end = ref_pos + fde_length;
+ cie_ref = fde_dwarf64 ? dio_ReadU8() : dio_ReadU4();
+ if (rules.eh_frame) fde_flag = cie_ref != 0;
+ else if (fde_dwarf64) fde_flag = cie_ref != ~(U8_T)0;
+ else fde_flag = cie_ref != ~(U4_T)0;
+ assert(fde_flag);
+ if (fde_flag) {
+ U8_T Addr, Range;
+ ELF_Section * sec = NULL;
+ if (rules.eh_frame) cie_ref = ref_pos - cie_ref;
+ if (cie_ref != rules.cie_pos) read_frame_cie(fde_pos, cie_ref);
+ Addr = read_frame_data_pointer(rules.addr_encoding, &sec);
+ Range = read_frame_data_pointer(rules.addr_encoding, NULL);
+ assert(Addr <= IP && Addr + Range > IP);
+ if (Addr <= IP && Addr + Range > IP) {
+ U8_T location0 = Addr;
+ if (rules.cie_aug != NULL && rules.cie_aug[0] == 'z') {
+ rules.fde_aug_length = dio_ReadULEB128();
+ rules.fde_aug_data = dio_GetDataPtr();
+ dio_Skip(rules.fde_aug_length);
+ }
+ copy_register_rules(&frame_regs, &cie_regs);
+ rules.location = Addr;
+ regs_stack_pos = 0;
+ for (;;) {
+ if (dio_GetPos() >= fde_end) {
+ rules.location = Addr + Range;
+ break;
+ }
+ exec_stack_frame_instruction();
+ assert(location0 <= IP);
+ if (rules.location > IP) break;
+ location0 = rules.location;
+ }
+ dwarf_stack_trace_addr = location0;
+ dwarf_stack_trace_size = rules.location - location0;
+ generate_commands();
+ }
+ }
+ dio_ExitSection();
+}
+
+static int cmp_frame_info_ranges(const void * x, const void * y) {
+ FrameInfoRange * rx = (FrameInfoRange *)x;
+ FrameInfoRange * ry = (FrameInfoRange *)y;
+ if (rx->mAddr < ry->mAddr) return -1;
+ if (rx->mAddr > ry->mAddr) return +1;
+ return 0;
+}
+
+static void create_search_index(DWARFCache * cache, ELF_Section * section) {
+ dio_EnterSection(NULL, section, 0);
+ while (dio_GetPos() < section->size) {
+ int fde_dwarf64 = 0;
+ U8_T fde_length = 0;
+ U8_T fde_pos = 0;
+ U8_T fde_end = 0;
+ U8_T ref_pos = 0;
+ U8_T cie_ref = 0;
+ int fde_flag = 0;
+
+ fde_pos = dio_GetPos();
+ fde_length = dio_ReadU4();
+ if (fde_length == 0) continue;
+ if (fde_length == ~(U4_T)0) {
+ fde_length = dio_ReadU8();
+ fde_dwarf64 = 1;
+ }
+ ref_pos = dio_GetPos();
+ fde_end = ref_pos + fde_length;
+ if (fde_end > rules.section->size) {
+ U4_T alignment = section->alignment;
+ if (alignment > 1 && fde_pos % alignment != 0) {
+ /* Workaround for sections with invalid alignment */
+ dio_SetPos(fde_pos + alignment - fde_pos % alignment);
+ continue;
+ }
+ else {
+ char msg[256];
+ snprintf(msg, sizeof(msg),
+ "Invalid length 0x%" PRIX64
+ " in FDE at 0x%" PRIX64, fde_length, fde_pos);
+ str_exception(ERR_INV_DWARF, msg);
+ }
+ }
+ cie_ref = fde_dwarf64 ? dio_ReadU8() : dio_ReadU4();
+ if (rules.eh_frame) fde_flag = cie_ref != 0;
+ else if (fde_dwarf64) fde_flag = cie_ref != ~(U8_T)0;
+ else fde_flag = cie_ref != ~(U4_T)0;
+ if (fde_flag) {
+ ELF_Section * sec = NULL;
+ FrameInfoRange * range = NULL;
+ if (rules.eh_frame) cie_ref = ref_pos - cie_ref;
+ if (cie_ref != rules.cie_pos) read_frame_cie(fde_pos, cie_ref);
+ if (cache->mFrameInfoRangesCnt >= cache->mFrameInfoRangesMax) {
+ cache->mFrameInfoRangesMax += 512;
+ if (cache->mFrameInfoRanges == NULL) cache->mFrameInfoRangesMax += (unsigned)(section->size / 32);
+ cache->mFrameInfoRanges = (FrameInfoRange *)loc_realloc(cache->mFrameInfoRanges,
+ cache->mFrameInfoRangesMax * sizeof(FrameInfoRange));
+ }
+ range = cache->mFrameInfoRanges + cache->mFrameInfoRangesCnt++;
+ range->mAddr = (ContextAddress)read_frame_data_pointer(rules.addr_encoding, &sec);
+ range->mSize = (ContextAddress)read_frame_data_pointer(rules.addr_encoding, NULL);
+ range->mOffset = fde_pos;
+ }
+ dio_SetPos(fde_end);
+ }
+ dio_ExitSection();
+ qsort(cache->mFrameInfoRanges, cache->mFrameInfoRangesCnt, sizeof(FrameInfoRange), cmp_frame_info_ranges);
+}
+
+void get_dwarf_stack_frame_info(Context * ctx, ELF_File * file, ELF_Section * sec, U8_T IP) {
+ DWARFCache * cache = get_dwarf_cache(file);
+ ELF_Section * section = cache->mDebugFrame;
+ unsigned l, h;
+
+ dwarf_stack_trace_regs_cnt = 0;
+ if (dwarf_stack_trace_fp == NULL) {
+ dwarf_stack_trace_fp = (StackTracingCommandSequence *)loc_alloc_zero(sizeof(StackTracingCommandSequence));
+ dwarf_stack_trace_fp->cmds_max = 1;
+ }
+ dwarf_stack_trace_fp->cmds_cnt = 0;
+ dwarf_stack_trace_addr = 0;
+ dwarf_stack_trace_size = 0;
+
+ if (section == NULL) section = cache->mEHFrame;
+ if (section == NULL) return;
+
+ memset(&rules, 0, sizeof(StackFrameRules));
+ rules.ctx = ctx;
+ rules.section = section;
+ rules.eh_frame = section == cache->mEHFrame;
+ rules.reg_id_scope.big_endian = file->big_endian;
+ rules.reg_id_scope.machine = file->machine;
+ rules.reg_id_scope.os_abi = file->os_abi;
+ rules.reg_id_scope.id_type = rules.eh_frame ? REGNUM_EH_FRAME : REGNUM_DWARF;
+ rules.cie_pos = ~(U8_T)0;
+
+ if (cache->mFrameInfoRanges == NULL) create_search_index(cache, section);
+ l = 0;
+ h = cache->mFrameInfoRangesCnt;
+ while (l < h) {
+ unsigned k = (l + h) / 2;
+ FrameInfoRange * range = cache->mFrameInfoRanges + k;
+ assert(cache->mFrameInfoRanges[l].mAddr <= cache->mFrameInfoRanges[h - 1].mAddr);
+ if (range->mAddr > IP) {
+ h = k;
+ }
+ else if (range->mAddr + range->mSize <= IP) {
+ l = k + 1;
+ }
+ else {
+ read_frame_fde(section, IP, range->mOffset);
+ return;
+ }
+ }
+ if (sec != NULL && sec->name != NULL && strcmp(sec->name, ".plt") == 0) {
+ assert(IP >= sec->addr);
+ assert(IP < sec->addr + sec->size);
+ if (generate_plt_section_commands(IP - sec->addr)) return;
+ }
+}
+
+#endif /* ENABLE_ELF && ENABLE_DebugContext */
diff --git a/agent/tcf/services/dwarfframe.h b/agent/tcf/services/dwarfframe.h
new file mode 100644
index 00000000..c4107f2a
--- /dev/null
+++ b/agent/tcf/services/dwarfframe.h
@@ -0,0 +1,56 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+
+/*
+ * This module implements handling of .debug_frame and .eh_frame sections.
+ *
+ * Functions in this module use exceptions to report errors, see exceptions.h
+ */
+
+#ifndef D_dwarfframe
+#define D_dwarfframe
+
+#include <config.h>
+
+#if ENABLE_ELF && ENABLE_DebugContext
+
+#include <framework/context.h>
+#include <services/dwarfcache.h>
+
+/*
+ * Lookup stack tracing information in ELF file, in .debug_frame and .eh_frame sections.
+ *
+ * Given register values in one frame, stack tracing information allows to calculate
+ * frame address and register values in the next frame.
+ *
+ * When function returns, dwarf_stack_trace_fp contains commands to calculate frame address,
+ * and dwarf_stack_trace_regs contains commands to calculate register values.
+ * In case of error reading frame data, the function throws an exception.
+ *
+ * 'ip' is link-time instruction address.
+ */
+extern void get_dwarf_stack_frame_info(Context * ctx, ELF_File * file, ELF_Section * sec, U8_T ip);
+
+extern U8_T dwarf_stack_trace_addr;
+extern U8_T dwarf_stack_trace_size;
+
+extern StackTracingCommandSequence * dwarf_stack_trace_fp;
+
+extern int dwarf_stack_trace_regs_cnt;
+extern StackTracingCommandSequence ** dwarf_stack_trace_regs;
+
+#endif /* ENABLE_ELF && ENABLE_DebugContext */
+
+#endif /* D_dwarfframe */
diff --git a/agent/tcf/services/dwarfio.c b/agent/tcf/services/dwarfio.c
new file mode 100644
index 00000000..6c5a35c4
--- /dev/null
+++ b/agent/tcf/services/dwarfio.c
@@ -0,0 +1,764 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 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
+ *******************************************************************************/
+
+/*
+ * This module implements low-level functions for reading DWARF debug information.
+ *
+ * Functions in this module use exceptions to report errors, see exceptions.h
+ */
+
+#include <config.h>
+
+#if ENABLE_ELF
+
+#include <assert.h>
+#include <string.h>
+#include <framework/myalloc.h>
+#include <framework/exceptions.h>
+#include <services/dwarfio.h>
+#include <services/dwarfreloc.h>
+#include <services/dwarf.h>
+
+#define ABBREV_TABLE_SIZE (4 * MEM_USAGE_FACTOR - 1)
+
+struct DIO_Abbreviation {
+ U2_T mTag;
+ U1_T mChildren;
+ U4_T mAttrLen;
+ U2_T mAttrs[2];
+};
+
+typedef struct DIO_Abbreviation DIO_Abbreviation;
+
+struct DIO_AbbrevSet {
+ U8_T mOffset;
+ U4_T mSize;
+ DIO_Abbreviation ** mTable;
+ struct DIO_AbbrevSet * mNext;
+};
+
+typedef struct DIO_AbbrevSet DIO_AbbrevSet;
+
+struct DIO_Cache {
+ U1_T * mStringTable;
+ U4_T mStringTableSize;
+ DIO_AbbrevSet ** mAbbrevTable;
+};
+
+typedef struct DIO_Cache DIO_Cache;
+
+U8_T dio_gEntryPos = 0;
+
+U8_T dio_gFormData = 0;
+size_t dio_gFormDataSize = 0;
+void * dio_gFormDataAddr = NULL;
+ELF_Section * dio_gFormSection = NULL;
+
+static ELF_Section * sSection;
+static int sBigEndian;
+static int sAddressSize;
+static int sRefAddressSize;
+static U1_T * sData;
+static U8_T sDataPos;
+static U8_T sDataLen;
+static DIO_UnitDescriptor * sUnit;
+
+static void dio_CloseELF(ELF_File * File) {
+ U4_T n, m;
+ DIO_Cache * Cache = (DIO_Cache *)File->dwarf_io_cache;
+
+ if (Cache == NULL) return;
+ if (Cache->mAbbrevTable != NULL) {
+ for (n = 0; n < ABBREV_TABLE_SIZE; n++) {
+ DIO_AbbrevSet * Set = Cache->mAbbrevTable[n];
+ while (Set != NULL) {
+ DIO_AbbrevSet * Next = Set->mNext;
+ for (m = 0; m < Set->mSize; m++) {
+ loc_free(Set->mTable[m]);
+ }
+ loc_free(Set->mTable);
+ loc_free(Set);
+ Set = Next;
+ }
+ }
+ loc_free(Cache->mAbbrevTable);
+ }
+ loc_free(Cache);
+ File->dwarf_io_cache = NULL;
+}
+
+static DIO_Cache * dio_GetCache(ELF_File * File) {
+ static int Inited = 0;
+ DIO_Cache * Cache = (DIO_Cache *)File->dwarf_io_cache;
+
+ if (!Inited) {
+ elf_add_close_listener(dio_CloseELF);
+ Inited = 1;
+ }
+ if (Cache == NULL) {
+ Cache = (DIO_Cache *)(File->dwarf_io_cache = loc_alloc_zero(sizeof(DIO_Cache)));
+ }
+ return Cache;
+}
+
+void dio_EnterSection(DIO_UnitDescriptor * Unit, ELF_Section * Section, U8_T Offset) {
+ if (elf_load(Section)) exception(errno);
+ sSection = Section;
+ sData = (U1_T *)Section->data;
+ sDataPos = Offset;
+ sDataLen = Section->size;
+ sBigEndian = Section->file->big_endian;
+ if (Unit != NULL) {
+ sAddressSize = Unit->mAddressSize;
+ if (Unit->mVersion < 3) sRefAddressSize = Unit->mAddressSize;
+ else sRefAddressSize = Unit->m64bit ? 8 : 4;
+ }
+ else if (Section->file->elf64) {
+ sAddressSize = 8;
+ sRefAddressSize = 8;
+ }
+ else {
+ sAddressSize = 4;
+ sRefAddressSize = 4;
+ }
+ sUnit = Unit;
+ dio_gEntryPos = 0;
+ assert(sData != NULL);
+ assert(sDataPos < sDataLen);
+}
+
+void dio_ExitSection() {
+ sSection = NULL;
+ sDataPos = 0;
+ sDataLen = 0;
+ sData = NULL;
+ sUnit = NULL;
+}
+
+U8_T dio_GetPos() {
+ return sDataPos;
+}
+
+U1_T * dio_GetDataPtr(void) {
+ return sData + sDataPos;
+}
+
+void dio_Skip(I8_T Bytes) {
+ if (sDataPos + Bytes > sDataLen) exception(ERR_EOF);
+ sDataPos += Bytes;
+}
+
+void dio_SetPos(U8_T Pos) {
+ if (Pos > sDataLen) exception(ERR_EOF);
+ sDataPos = Pos;
+}
+
+void dio_Read(U1_T * Buf, U4_T Size) {
+ if (sDataPos + Size > sDataLen) exception(ERR_EOF);
+ memcpy(Buf, sData + sDataPos, Size);
+ sDataPos += Size;
+}
+
+static U1_T dio_ReadU1F(void) {
+ if (sDataPos >= sDataLen) exception(ERR_EOF);
+ return sData[sDataPos++];
+}
+
+U1_T dio_ReadU1(void) {
+ return sDataPos < sDataLen ? sData[sDataPos++] : dio_ReadU1F();
+}
+
+#define dio_ReadU1() (sDataPos < sDataLen ? sData[sDataPos++] : dio_ReadU1F())
+
+U2_T dio_ReadU2(void) {
+ U2_T x0 = dio_ReadU1();
+ U2_T x1 = dio_ReadU1();
+ return sBigEndian ? (x0 << 8) | x1 : x0 | (x1 << 8);
+}
+
+U4_T dio_ReadU4(void) {
+ U4_T x0 = dio_ReadU1();
+ U4_T x1 = dio_ReadU1();
+ U4_T x2 = dio_ReadU1();
+ U4_T x3 = dio_ReadU1();
+ return sBigEndian ?
+ (x0 << 24) | (x1 << 16) | (x2 << 8) | x3:
+ x0 | (x1 << 8) | (x2 << 16) | (x3 << 24);
+}
+
+U8_T dio_ReadU8(void) {
+ U8_T x0 = dio_ReadU4();
+ U8_T x1 = dio_ReadU4();
+ return sBigEndian ? (x0 << 32) | x1 : x0 | (x1 << 32);
+}
+
+U4_T dio_ReadULEB128(void) {
+ U4_T Res = 0;
+ int i = 0;
+ for (;; i += 7) {
+ U1_T n = dio_ReadU1();
+ Res |= (n & 0x7Fu) << i;
+ if ((n & 0x80) == 0) break;
+ }
+ return Res;
+}
+
+I4_T dio_ReadSLEB128(void) {
+ U4_T Res = 0;
+ int i = 0;
+ for (;; i += 7) {
+ U1_T n = dio_ReadU1();
+ Res |= (n & 0x7Fu) << i;
+ if ((n & 0x80) == 0) {
+ Res |= -(n & 0x40) << i;
+ break;
+ }
+ }
+ return (I4_T)Res;
+}
+
+U8_T dio_ReadU8LEB128(void) {
+ U8_T Res = 0;
+ int i = 0;
+ for (;; i += 7) {
+ U1_T n = dio_ReadU1();
+ Res |= (n & 0x7Fu) << i;
+ if ((n & 0x80) == 0) break;
+ }
+ return Res;
+}
+
+I8_T dio_ReadS8LEB128(void) {
+ U8_T Res = 0;
+ int i = 0;
+ for (;; i += 7) {
+ U1_T n = dio_ReadU1();
+ Res |= (n & 0x7Fu) << i;
+ if ((n & 0x80) == 0) {
+ Res |= -(n & 0x40) << i;
+ break;
+ }
+ }
+ return (I8_T)Res;
+}
+
+U8_T dio_ReadUX(int Size) {
+ switch (Size) {
+ case 2:
+ return dio_ReadU2();
+ case 4:
+ return dio_ReadU4();
+ case 8:
+ return dio_ReadU8();
+ default:
+ str_exception(ERR_INV_DWARF, "invalid data size");;
+ return 0;
+ }
+}
+
+U8_T dio_ReadAddressX(ELF_Section ** s, int size) {
+ U8_T pos = sDataPos;
+ switch (size) {
+ case 2: {
+ U2_T x = dio_ReadU2();
+ drl_relocate(sSection, pos, &x, sizeof(x), s);
+ return x;
+ }
+ case 4: {
+ U4_T x = dio_ReadU4();
+ drl_relocate(sSection, pos, &x, sizeof(x), s);
+ return x;
+ }
+ case 8: {
+ U8_T x = dio_ReadU8();
+ drl_relocate(sSection, pos, &x, sizeof(x), s);
+ return x;
+ }
+ default:
+ str_exception(ERR_INV_DWARF, "invalid data size");;
+ return 0;
+ }
+}
+
+U8_T dio_ReadAddress(ELF_Section ** s) {
+ return dio_ReadAddressX(s, sAddressSize);
+}
+
+char * dio_ReadString(void) {
+ char * Res = (char *)(sData + sDataPos);
+ U4_T Length = 0;
+ while (dio_ReadU1() != 0) Length++;
+ if (Length == 0) return NULL;
+ return Res;
+}
+
+static U1_T * dio_LoadStringTable(U4_T * StringTableSize) {
+ ELF_File * File = sSection->file;
+ DIO_Cache * Cache = dio_GetCache(File);
+
+ if (Cache->mStringTable == NULL) {
+ U4_T ID;
+ ELF_Section * Section = NULL;
+
+ for (ID = 1; ID < File->section_cnt; ID++) {
+ if (strcmp(File->sections[ID].name, ".debug_str") == 0) {
+ if (Section != NULL) {
+ str_exception(ERR_INV_DWARF, "more then one .debug_str section in a file");
+ }
+ Section = File->sections + ID;
+ assert(Section->file == File);
+ }
+ }
+
+ if (Section == NULL) {
+ str_exception(ERR_INV_DWARF, "section .debug_str not found");
+ }
+
+ Cache->mStringTableSize = (size_t)Section->size;
+ if (elf_load(Section) < 0) {
+ str_exception(ERR_INV_DWARF, "invalid .debug_str section");
+ }
+ Cache->mStringTable = (U1_T *)Section->data;
+ }
+
+ *StringTableSize = Cache->mStringTableSize;
+ return Cache->mStringTable;
+}
+
+static void dio_ReadFormAddr(void) {
+ dio_gFormData = dio_ReadAddressX(&dio_gFormSection, sAddressSize);
+ dio_gFormDataSize = sAddressSize;
+}
+
+static void dio_ReadFormBlock(U4_T Size) {
+ dio_gFormDataAddr = sData + sDataPos;
+ dio_gFormDataSize = Size;
+ if (sDataPos + Size > sDataLen) exception(ERR_EOF);
+ sDataPos += Size;
+}
+
+static void dio_ReadFormData(U1_T Size, U8_T Data) {
+ dio_gFormDataAddr = sData + sDataPos - Size;
+ dio_gFormData = Data;
+ dio_gFormDataSize = Size;
+}
+
+static void dio_ReadFormRef(void) {
+ dio_gFormData = dio_ReadU4();
+ dio_gFormDataSize = 4;
+}
+
+static void dio_ReadFormRelRef(U8_T Offset) {
+ if (sUnit->mUnitSize > 0 && Offset >= sUnit->mUnitSize) {
+ str_exception(ERR_INV_DWARF, "invalid REF attribute value");
+ }
+ dio_gFormData = sSection->addr + sUnit->mUnitOffs + Offset;
+ dio_gFormDataSize = sAddressSize;
+}
+
+static void dio_ReadFormRefAddr(void) {
+ dio_gFormData = dio_ReadAddressX(&dio_gFormSection, sRefAddressSize);
+ dio_gFormDataSize = sRefAddressSize;
+}
+
+static void dio_ReadFormString(void) {
+ dio_gFormDataAddr = sData + sDataPos;
+ dio_gFormDataSize = 1;
+ while (dio_ReadU1()) dio_gFormDataSize++;
+}
+
+static void dio_ReadFormStringRef(void) {
+ U8_T Offset = dio_ReadUX(sUnit->m64bit ? 8 : 4);
+ U4_T StringTableSize = 0;
+ U1_T * StringTable = dio_LoadStringTable(&StringTableSize);
+ dio_gFormDataAddr = StringTable + Offset;
+ dio_gFormDataSize = 1;
+ for (;;) {
+ if (Offset >= StringTableSize) {
+ str_exception(ERR_INV_DWARF, "invalid FORM_STRP attribute");
+ }
+ if (StringTable[Offset++] == 0) break;
+ dio_gFormDataSize++;
+ }
+}
+
+void dio_ReadAttribute(U2_T Attr, U2_T Form) {
+ dio_gFormSection = NULL;
+ dio_gFormDataAddr = NULL;
+ dio_gFormDataSize = 0;
+ dio_gFormData = 0;
+ if (Attr == AT_stmt_list && sSection->relocate) {
+ U4_T Size = 0;
+ switch (Form) {
+ case FORM_DATA2 : Size = 2; break;
+ case FORM_DATA4 : Size = 4; break;
+ case FORM_DATA8 : Size = 8; break;
+ default: str_exception(ERR_INV_DWARF, "invalid FORM of DW_AT_stmt_list");
+ }
+ dio_gFormData = dio_ReadAddressX(&dio_gFormSection, Size);
+ dio_gFormDataSize = Size;
+ return;
+ }
+ switch (Form) {
+ case FORM_ADDR : dio_ReadFormAddr(); break;
+ case FORM_REF : dio_ReadFormRef(); break;
+ case FORM_BLOCK1 : dio_ReadFormBlock(dio_ReadU1()); break;
+ case FORM_BLOCK2 : dio_ReadFormBlock(dio_ReadU2()); break;
+ case FORM_BLOCK4 : dio_ReadFormBlock(dio_ReadU4()); break;
+ case FORM_BLOCK : dio_ReadFormBlock(dio_ReadULEB128()); break;
+ case FORM_DATA1 : dio_ReadFormData(1, dio_ReadU1()); break;
+ case FORM_DATA2 : dio_ReadFormData(2, dio_ReadU2()); break;
+ case FORM_DATA4 : dio_ReadFormData(4, dio_ReadU4()); break;
+ case FORM_DATA8 : dio_ReadFormData(8, dio_ReadU8()); break;
+ case FORM_SDATA : dio_ReadFormData(8, dio_ReadS8LEB128()); dio_gFormDataAddr = NULL; break;
+ case FORM_UDATA : dio_ReadFormData(8, dio_ReadU8LEB128()); dio_gFormDataAddr = NULL; break;
+ case FORM_FLAG : dio_ReadFormData(1, dio_ReadU1()); break;
+ case FORM_STRING : dio_ReadFormString(); break;
+ case FORM_STRP : dio_ReadFormStringRef(); break;
+ case FORM_REF_ADDR : dio_ReadFormRefAddr(); break;
+ case FORM_REF1 : dio_ReadFormRelRef(dio_ReadU1()); break;
+ case FORM_REF2 : dio_ReadFormRelRef(dio_ReadU2()); break;
+ case FORM_REF4 : dio_ReadFormRelRef(dio_ReadU4()); break;
+ case FORM_REF8 : dio_ReadFormRelRef(dio_ReadU8()); break;
+ case FORM_REF_UDATA : dio_ReadFormRelRef(dio_ReadULEB128()); break;
+ default: str_exception(ERR_INV_DWARF, "invalid FORM");
+ }
+}
+
+int dio_ReadEntry(DIO_EntryCallBack CallBack, U2_T TargetAttr) {
+ DIO_Abbreviation * Abbr = NULL;
+ U2_T Tag = 0;
+ U4_T AttrPos = 0;
+ U4_T EntrySize = 0;
+ int Init = 1;
+
+ dio_gEntryPos = sDataPos;
+ if (sUnit->mVersion >= 2) {
+ U4_T AbbrCode = dio_ReadULEB128();
+ if (AbbrCode == 0) return 0;
+ if (AbbrCode >= sUnit->mAbbrevTableSize || sUnit->mAbbrevTable[AbbrCode] == NULL) {
+ str_exception(ERR_INV_DWARF, "invalid abbreviation code");
+ }
+ Abbr = sUnit->mAbbrevTable[AbbrCode];
+ Tag = Abbr->mTag;
+ }
+ else {
+ EntrySize = dio_ReadU4();
+ if (EntrySize < 8) {
+ while (EntrySize > 4) {
+ dio_ReadU1();
+ EntrySize--;
+ }
+ return 0;
+ }
+ Tag = dio_ReadU2();
+ }
+ for (;;) {
+ U2_T Attr = 0;
+ U2_T Form = 0;
+ if (Init) {
+ Form = DWARF_ENTRY_NO_CHILDREN;
+ if (Abbr != NULL && Abbr->mChildren) Form = DWARF_ENTRY_HAS_CHILDREN;
+ Init = 0;
+ }
+ else if (Abbr != NULL) {
+ if (AttrPos < Abbr->mAttrLen) {
+ Attr = Abbr->mAttrs[AttrPos++];
+ Form = Abbr->mAttrs[AttrPos++];
+ if (Form == FORM_INDIRECT) Form = (U2_T)dio_ReadULEB128();
+ }
+ }
+ else if (sDataPos <= dio_gEntryPos + EntrySize - 2) {
+ if (sBigEndian) {
+ Attr = (U2_T)sData[sDataPos++] << 8;
+ Attr |= (U2_T)sData[sDataPos++];
+ }
+ else {
+ Attr = (U2_T)sData[sDataPos++];
+ Attr |= (U2_T)sData[sDataPos++] << 8;
+ }
+ Form = Attr & 0xF;
+ Attr = (Attr & 0xfff0) >> 4;
+ }
+ if (TargetAttr && Attr != TargetAttr) {
+ /* Shortcut for attributes that the caller is not interested in */
+ switch (Attr) {
+ case 0:
+ if (Form != 0) continue;
+ return 1;
+ case AT_specification_v1:
+ case AT_specification_v2:
+ case AT_abstract_origin:
+ break;
+ default:
+ switch (Form) {
+ case FORM_ADDR : sDataPos += sAddressSize; continue;
+ case FORM_REF : sDataPos += 4; continue;
+ case FORM_BLOCK1 : sDataPos += dio_ReadU1F(); continue;
+ case FORM_BLOCK2 : sDataPos += dio_ReadU2(); continue;
+ case FORM_BLOCK4 : sDataPos += dio_ReadU4(); continue;
+ case FORM_BLOCK : sDataPos += dio_ReadULEB128(); continue;
+ case FORM_DATA1 : sDataPos++; continue;
+ case FORM_DATA2 : sDataPos += 2; continue;
+ case FORM_DATA4 : sDataPos += 4; continue;
+ case FORM_DATA8 : sDataPos += 8; continue;
+ case FORM_SDATA : dio_ReadS8LEB128(); continue;
+ case FORM_UDATA : dio_ReadU8LEB128(); continue;
+ case FORM_FLAG : sDataPos++; continue;
+ case FORM_STRING : dio_ReadFormString(); continue;
+ case FORM_STRP : sDataPos += (sUnit->m64bit ? 8 : 4); continue;
+ case FORM_REF_ADDR : sDataPos += sRefAddressSize; continue;
+ case FORM_REF1 : sDataPos++; continue;
+ case FORM_REF2 : sDataPos += 2; continue;
+ case FORM_REF4 : sDataPos += 4; continue;
+ case FORM_REF8 : sDataPos += 8; continue;
+ case FORM_REF_UDATA : dio_ReadULEB128(); continue;
+ }
+ }
+ }
+ if (Attr != 0 && Form != 0) dio_ReadAttribute(Attr, Form);
+ if (Tag == TAG_compile_unit) {
+ if (Attr == AT_sibling && sUnit->mUnitSize == 0) {
+ dio_ChkRef(Form);
+ assert(sUnit->mVersion == 1);
+ sUnit->mUnitSize = (U4_T)(dio_gFormData - sSection->addr - sUnit->mUnitOffs);
+ assert(sUnit->mUnitOffs < sDataPos);
+ assert(sUnit->mUnitOffs + sUnit->mUnitSize >= sDataPos);
+ }
+ else if (Attr == 0 && Form == 0) {
+ if (sUnit->mUnitSize == 0) str_exception(ERR_INV_DWARF, "missing compilation unit sibling attribute");
+ }
+ }
+ CallBack(Tag, Attr, Form);
+ if (Attr == 0 && Form == 0) break;
+ }
+ return 1;
+}
+
+static void dio_FindAbbrevTable(void);
+
+void dio_ReadUnit(DIO_UnitDescriptor * Unit, DIO_EntryCallBack CallBack) {
+ memset(Unit, 0, sizeof(DIO_UnitDescriptor));
+ sUnit = Unit;
+ sUnit->mSection = sSection;
+ sUnit->mUnitOffs = sDataPos;
+ sUnit->m64bit = 0;
+ if (strcmp(sSection->name, ".debug") != 0) {
+ ELF_Section * Sect = NULL;
+ sUnit->mUnitSize = dio_ReadU4();
+ if (sUnit->mUnitSize == 0xffffffffu) {
+ sUnit->m64bit = 1;
+ sUnit->mUnitSize = dio_ReadU8();
+ sUnit->mUnitSize += 12;
+ }
+ else {
+ sUnit->mUnitSize += 4;
+ }
+ sUnit->mVersion = dio_ReadU2();
+ sUnit->mAbbrevTableOffs = (U4_T)dio_ReadAddressX(&Sect, 4);
+ sUnit->mAddressSize = dio_ReadU1();
+ dio_FindAbbrevTable();
+ }
+ else {
+ sUnit->mVersion = 1;
+ sUnit->mAddressSize = 4;
+ }
+ while (sUnit->mUnitSize == 0 || sDataPos < sUnit->mUnitOffs + sUnit->mUnitSize) {
+ dio_ReadEntry(CallBack, 0);
+ }
+ sUnit = NULL;
+}
+
+#define dio_AbbrevTableHash(Offset) (((unsigned)(Offset)) / 16 % ABBREV_TABLE_SIZE)
+
+void dio_LoadAbbrevTable(ELF_File * File) {
+ U4_T ID;
+ U8_T TableOffset = 0;
+ ELF_Section * Section = NULL;
+ static U2_T * AttrBuf = NULL;
+ static U4_T AttrBufSize = 0;
+ static DIO_Abbreviation ** AbbrevBuf = NULL;
+ static U4_T AbbrevBufSize = 0;
+ U4_T AbbrevBufPos = 0;
+ DIO_Cache * Cache = dio_GetCache(File);
+
+ if (Cache->mAbbrevTable != NULL) return;
+ Cache->mAbbrevTable = (DIO_AbbrevSet **)loc_alloc_zero(sizeof(DIO_AbbrevSet *) * ABBREV_TABLE_SIZE);
+
+ for (ID = 1; ID < File->section_cnt; ID++) {
+ if (strcmp(File->sections[ID].name, ".debug_abbrev") == 0) {
+ if (Section != NULL) {
+ str_exception(ERR_INV_DWARF, "more then one .debug_abbrev section in a file");
+ }
+ Section = File->sections + ID;
+ }
+ }
+ if (Section == NULL) return;
+ dio_EnterSection(NULL, Section, 0);
+ for (;;) {
+ U4_T AttrPos = 0;
+ U2_T Tag = 0;
+ U1_T Children = 0;
+ U4_T ID = dio_ReadULEB128();
+ if (ID == 0) {
+ /* End of compilation unit */
+ U4_T Hash = dio_AbbrevTableHash(TableOffset);
+ DIO_AbbrevSet * AbbrevSet = (DIO_AbbrevSet *)loc_alloc_zero(sizeof(DIO_AbbrevSet));
+ AbbrevSet->mOffset = TableOffset;
+ AbbrevSet->mTable = (DIO_Abbreviation **)loc_alloc(sizeof(DIO_Abbreviation *) * AbbrevBufPos);
+ AbbrevSet->mSize = AbbrevBufPos;
+ AbbrevSet->mNext = Cache->mAbbrevTable[Hash];
+ Cache->mAbbrevTable[Hash] = AbbrevSet;
+ memcpy(AbbrevSet->mTable, AbbrevBuf, sizeof(DIO_Abbreviation *) * AbbrevBufPos);
+ memset(AbbrevBuf, 0, sizeof(DIO_Abbreviation *) * AbbrevBufPos);
+ AbbrevBufPos = 0;
+ if (sDataPos >= Section->size) break;
+ TableOffset = sDataPos;
+ continue;
+ }
+ if (ID >= 0x1000000) str_exception(ERR_INV_DWARF, "invalid abbreviation table");
+ if (ID >= AbbrevBufPos) {
+ U4_T Pos = AbbrevBufPos;
+ AbbrevBufPos = ID + 1;
+ if (AbbrevBufPos > AbbrevBufSize) {
+ U4_T Size = AbbrevBufSize;
+ AbbrevBufSize = AbbrevBufPos + 128;
+ AbbrevBuf = (DIO_Abbreviation **)loc_realloc(AbbrevBuf, sizeof(DIO_Abbreviation *) * AbbrevBufSize);
+ memset(AbbrevBuf + Size, 0, sizeof(DIO_Abbreviation *) * (AbbrevBufSize - Size));
+ }
+ while (Pos < AbbrevBufPos) {
+ loc_free(AbbrevBuf[Pos]);
+ AbbrevBuf[Pos] = NULL;
+ Pos++;
+ }
+ }
+ Tag = (U2_T)dio_ReadULEB128();
+ Children = (U2_T)dio_ReadU1() != 0;
+ for (;;) {
+ U4_T Attr = dio_ReadULEB128();
+ U4_T Form = dio_ReadULEB128();
+ if (Attr >= 0x10000 || Form >= 0x10000) str_exception(ERR_INV_DWARF, "invalid abbreviation table");
+ if (Attr == 0 && Form == 0) {
+ DIO_Abbreviation * Abbr;
+ if (AbbrevBuf[ID] != NULL) str_exception(ERR_INV_DWARF, "invalid abbreviation table");
+ Abbr = (DIO_Abbreviation *)loc_alloc_zero(sizeof(DIO_Abbreviation) - sizeof(U2_T) * 2 + sizeof(U2_T) * AttrPos);
+ Abbr->mTag = Tag;
+ Abbr->mChildren = Children;
+ Abbr->mAttrLen = AttrPos;
+ memcpy(Abbr->mAttrs, AttrBuf, sizeof(U2_T) * AttrPos);
+ AbbrevBuf[ID] = Abbr;
+ break;
+ }
+ if (AttrBufSize < AttrPos + 2) {
+ AttrBufSize = AttrPos + 256;
+ AttrBuf = (U2_T *)loc_realloc(AttrBuf, sizeof(U2_T) * AttrBufSize);
+ }
+ AttrBuf[AttrPos++] = (U2_T)Attr;
+ AttrBuf[AttrPos++] = (U2_T)Form;
+ }
+ }
+ assert(AbbrevBufPos == 0);
+ dio_ExitSection();
+}
+
+static void dio_FindAbbrevTable(void) {
+ DIO_Cache * Cache = dio_GetCache(sSection->file);
+ if (Cache->mAbbrevTable != NULL) {
+ U4_T Hash = dio_AbbrevTableHash(sUnit->mAbbrevTableOffs);
+ DIO_AbbrevSet * AbbrevSet = Cache->mAbbrevTable[Hash];
+ while (AbbrevSet != NULL) {
+ if (AbbrevSet->mOffset == sUnit->mAbbrevTableOffs) {
+ sUnit->mAbbrevTable = AbbrevSet->mTable;
+ sUnit->mAbbrevTableSize = AbbrevSet->mSize;
+ return;
+ }
+ AbbrevSet = AbbrevSet->mNext;
+ }
+ }
+ sUnit->mAbbrevTable = NULL;
+ sUnit->mAbbrevTableSize = 0;
+ str_exception(ERR_INV_DWARF, "invalid abbreviation table offset");
+}
+
+void dio_ChkFlag(U2_T Form) {
+ switch (Form) {
+ case FORM_FLAG :
+ return;
+ }
+ str_exception(ERR_INV_DWARF, "FORM_FLAG expected");
+}
+
+void dio_ChkRef(U2_T Form) {
+ switch (Form) {
+ case FORM_REF :
+ case FORM_REF_ADDR :
+ case FORM_REF1 :
+ case FORM_REF2 :
+ case FORM_REF4 :
+ case FORM_REF8 :
+ case FORM_REF_UDATA :
+ return;
+ }
+ str_exception(ERR_INV_DWARF, "FORM_REF expected");
+}
+
+void dio_ChkAddr(U2_T Form) {
+ switch (Form) {
+ case FORM_ADDR :
+ return;
+ }
+ str_exception(ERR_INV_DWARF, "FORM_ADDR expected");
+}
+
+void dio_ChkData(U2_T Form) {
+ switch (Form) {
+ case FORM_DATA1 :
+ case FORM_DATA2 :
+ case FORM_DATA4 :
+ case FORM_DATA8 :
+ case FORM_SDATA :
+ case FORM_UDATA :
+ return;
+ }
+ str_exception(ERR_INV_DWARF, "FORM_DATA expected");
+}
+
+void dio_ChkBlock(U2_T Form, U1_T ** Buf, size_t * Size) {
+ switch (Form) {
+ case FORM_BLOCK1 :
+ case FORM_BLOCK2 :
+ case FORM_BLOCK4 :
+ case FORM_BLOCK :
+ case FORM_DATA1 :
+ case FORM_DATA2 :
+ case FORM_DATA4 :
+ case FORM_DATA8 :
+ assert(dio_gFormDataAddr >= sSection->data);
+ assert((U1_T *)dio_gFormDataAddr < (U1_T *)sSection->data + sSection->size);
+ *Size = dio_gFormDataSize;
+ *Buf = (U1_T *)dio_gFormDataAddr;
+ break;
+ default:
+ str_exception(ERR_INV_DWARF, "FORM_BLOCK expected");
+ }
+}
+
+void dio_ChkString(U2_T Form) {
+ if (Form == FORM_STRING) return;
+ if (Form == FORM_STRP) return;
+ str_exception(ERR_INV_DWARF, "FORM_STRING expected");
+}
+
+#endif /* ENABLE_ELF */
diff --git a/agent/tcf/services/dwarfio.h b/agent/tcf/services/dwarfio.h
new file mode 100644
index 00000000..8e617a1d
--- /dev/null
+++ b/agent/tcf/services/dwarfio.h
@@ -0,0 +1,107 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 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
+ *******************************************************************************/
+
+/*
+ * This module implements low-level functions for reading DWARF debug information.
+ *
+ * Functions in this module use exceptions to report errors, see exceptions.h
+ */
+#ifndef D_dwarfio
+#define D_dwarfio
+
+#include <config.h>
+
+#if ENABLE_ELF
+
+#include <services/tcf_elf.h>
+
+typedef struct DIO_UnitDescriptor {
+ ELF_Section * mSection;
+ U2_T mVersion;
+ U1_T m64bit;
+ U1_T mAddressSize;
+ U8_T mUnitOffs;
+ U8_T mUnitSize;
+ U4_T mAbbrevTableOffs;
+ struct DIO_Abbreviation ** mAbbrevTable;
+ U4_T mAbbrevTableSize;
+} DIO_UnitDescriptor;
+
+extern U8_T dio_gEntryPos;
+
+extern U8_T dio_gFormData;
+extern size_t dio_gFormDataSize;
+extern void * dio_gFormDataAddr;
+extern ELF_Section * dio_gFormSection;
+
+extern void dio_EnterSection(DIO_UnitDescriptor * Unit, ELF_Section * Section, U8_T Offset);
+extern void dio_ExitSection(void);
+
+extern void dio_Skip(I8_T Bytes);
+extern void dio_SetPos(U8_T Pos);
+extern void dio_Read(U1_T * Buf, U4_T Size);
+extern U8_T dio_GetPos(void); /* Offset in the section */
+extern U1_T * dio_GetDataPtr(void);
+
+extern U1_T dio_ReadU1(void);
+extern U2_T dio_ReadU2(void);
+extern U4_T dio_ReadU4(void);
+extern U8_T dio_ReadU8(void);
+
+extern U4_T dio_ReadULEB128(void);
+extern I4_T dio_ReadSLEB128(void);
+extern U8_T dio_ReadU8LEB128(void);
+extern I8_T dio_ReadS8LEB128(void);
+
+extern U8_T dio_ReadUX(int Size);
+
+/*
+ * Read link-time address value.
+ * Relocation tables are used if necessary to compute the address value.
+ * For a file that can be relotated at run-time, 's' is assigned a section that the address points to.
+ */
+extern U8_T dio_ReadAddressX(ELF_Section ** s, int Size);
+extern U8_T dio_ReadAddress(ELF_Section ** s);
+
+extern char * dio_ReadString(void);
+
+extern void dio_ReadAttribute(U2_T Attr, U2_T Form);
+
+typedef void (*DIO_EntryCallBack)(U2_T /* Tag */, U2_T /* Attr */, U2_T /* Form */);
+/*
+ * CallBack is called berore each DWARF entry with Atrr = 0 and Form != 0,
+ * then is is called for each entry attribute with appropriate Attr and Form values,
+ * and then called after the entry with Attr = 0 and Form = 0.
+ * This sequence is repeated for each entry in the debug info unit.
+ */
+extern void dio_ReadUnit(DIO_UnitDescriptor * Unit, DIO_EntryCallBack CallBack);
+extern int dio_ReadEntry(DIO_EntryCallBack CallBack, U2_T Attr);
+
+/* CallBack 'Form' value on unit entry: */
+#define DWARF_ENTRY_NO_CHILDREN 2
+#define DWARF_ENTRY_HAS_CHILDREN 3
+
+extern void dio_LoadAbbrevTable(ELF_File * File);
+
+extern void dio_ChkFlag(U2_T Form);
+extern void dio_ChkRef(U2_T Form);
+extern void dio_ChkAddr(U2_T Form);
+extern void dio_ChkData(U2_T Form);
+extern void dio_ChkBlock(U2_T Form, U1_T ** Buf, size_t * Size);
+extern void dio_ChkString(U2_T Form);
+
+#endif /* ENABLE_ELF */
+
+#endif /* D_dwarfio */
diff --git a/agent/tcf/services/dwarfreloc.c b/agent/tcf/services/dwarfreloc.c
new file mode 100644
index 00000000..23377831
--- /dev/null
+++ b/agent/tcf/services/dwarfreloc.c
@@ -0,0 +1,236 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+
+/*
+ * This module implements ELF relocation records handling for reading DWARF debug information.
+ *
+ * Functions in this module use exceptions to report errors, see exceptions.h
+ */
+
+#include <config.h>
+
+#if ENABLE_ELF
+
+#include <assert.h>
+#include <framework/exceptions.h>
+#include <services/dwarfreloc.h>
+
+static ELF_Section * section = NULL;
+static ELF_Section * relocs = NULL;
+static ELF_Section * symbols = NULL;
+static ELF_Section ** destination_section = NULL;
+
+static U8_T reloc_offset = 0;
+static U4_T reloc_type = 0;
+static U8_T reloc_addend = 0;
+static U4_T sym_index = 0;
+static U8_T sym_value = 0;
+
+static void * data_buf = NULL;
+static size_t data_size = 0;
+
+typedef struct ElfRelocateFunc {
+ int machine;
+ void (*func)(void);
+} ElfRelocateFunc;
+
+#include <machine/elf-mdep.h>
+
+static void relocate(void * r) {
+ ElfRelocateFunc * func;
+ if (!relocs->file->elf64) {
+ if (relocs->type == SHT_REL) {
+ Elf32_Rel bf = *(Elf32_Rel *)r;
+ if (relocs->file->byte_swap) {
+ SWAP(bf.r_offset);
+ SWAP(bf.r_info);
+ }
+ sym_index = ELF32_R_SYM(bf.r_info);
+ reloc_type = ELF32_R_TYPE(bf.r_info);
+ reloc_addend = 0;
+ }
+ else {
+ Elf32_Rela bf = *(Elf32_Rela *)r;
+ if (relocs->file->byte_swap) {
+ SWAP(bf.r_offset);
+ SWAP(bf.r_info);
+ SWAP(bf.r_addend);
+ }
+ sym_index = ELF32_R_SYM(bf.r_info);
+ reloc_type = ELF32_R_TYPE(bf.r_info);
+ reloc_addend = bf.r_addend;
+ }
+ if (sym_index != STN_UNDEF) {
+ Elf32_Sym bf = ((Elf32_Sym *)symbols->data)[sym_index];
+ if (symbols->file->byte_swap) {
+ SWAP(bf.st_name);
+ SWAP(bf.st_value);
+ SWAP(bf.st_size);
+ SWAP(bf.st_info);
+ SWAP(bf.st_other);
+ SWAP(bf.st_shndx);
+ }
+ if (symbols->file->type != ET_EXEC) {
+ switch (bf.st_shndx) {
+ case SHN_ABS:
+ sym_value = bf.st_value;
+ break;
+ case SHN_COMMON:
+ case SHN_UNDEF:
+ str_exception(ERR_INV_FORMAT, "Invalid relocation record");
+ break;
+ default:
+ if (bf.st_shndx >= symbols->file->section_cnt) str_exception(ERR_INV_FORMAT, "Invalid relocation record");
+ sym_value = (symbols->file->sections + bf.st_shndx)->addr + bf.st_value;
+ *destination_section = symbols->file->sections + bf.st_shndx;
+ break;
+ }
+ }
+ else {
+ sym_value = bf.st_value;
+ }
+ }
+ }
+ else {
+ if (relocs->type == SHT_REL) {
+ Elf64_Rel bf = *(Elf64_Rel *)r;
+ if (relocs->file->byte_swap) {
+ SWAP(bf.r_offset);
+ SWAP(bf.r_info);
+ }
+ sym_index = ELF64_R_SYM(bf.r_info);
+ reloc_type = ELF64_R_TYPE(bf.r_info);
+ reloc_addend = 0;
+ }
+ else {
+ Elf64_Rela bf = *(Elf64_Rela *)r;
+ if (relocs->file->byte_swap) {
+ SWAP(bf.r_offset);
+ SWAP(bf.r_info);
+ SWAP(bf.r_addend);
+ }
+ sym_index = ELF64_R_SYM(bf.r_info);
+ reloc_type = ELF64_R_TYPE(bf.r_info);
+ reloc_addend = bf.r_addend;
+ }
+ if (sym_index != STN_UNDEF) {
+ Elf64_Sym bf = ((Elf64_Sym *)symbols->data)[sym_index];
+ if (symbols->file->byte_swap) {
+ SWAP(bf.st_name);
+ SWAP(bf.st_value);
+ SWAP(bf.st_size);
+ SWAP(bf.st_info);
+ SWAP(bf.st_other);
+ SWAP(bf.st_shndx);
+ }
+ if (symbols->file->type != ET_EXEC) {
+ switch (bf.st_shndx) {
+ case SHN_ABS:
+ sym_value = bf.st_value;
+ break;
+ case SHN_COMMON:
+ case SHN_UNDEF:
+ str_exception(ERR_INV_FORMAT, "Invalid relocation record");
+ break;
+ default:
+ if (bf.st_shndx >= symbols->file->section_cnt) str_exception(ERR_INV_FORMAT, "Invalid relocation record");
+ sym_value = (symbols->file->sections + bf.st_shndx)->addr + bf.st_value;
+ *destination_section = symbols->file->sections + bf.st_shndx;
+ break;
+ }
+ }
+ else {
+ sym_value = bf.st_value;
+ }
+ }
+ }
+
+ /* For executable file we don't need to apply the relocation,
+ * all we need is destination_section */
+ if (section->file->type != ET_REL) return;
+
+ func = elf_relocate_funcs;
+ while (func->machine != section->file->machine) {
+ if (func->func == NULL) str_exception(ERR_INV_FORMAT, "Unsupported ELF machine code");
+ func++;
+ }
+ func->func();
+}
+
+void drl_relocate(ELF_Section * s, U8_T offset, void * buf, size_t size, ELF_Section ** dst) {
+ unsigned i;
+ ELF_Section * d = NULL;
+
+ if (dst == NULL) dst = &d;
+ else *dst = NULL;
+ if (!s->relocate) return;
+
+ section = s;
+ destination_section = dst;
+ reloc_offset = offset;
+ data_buf = buf;
+ data_size = size;
+ for (i = 1; i < s->file->section_cnt; i++) {
+ ELF_Section * r = s->file->sections + i;
+ if (r->size == 0) continue;
+ if (r->type != SHT_REL && r->type != SHT_RELA) continue;
+ if (r->info == s->index) {
+ uint8_t * p;
+ uint8_t * q;
+ relocs = r;
+ symbols = s->file->sections + r->link;
+ if (elf_load(relocs) < 0) exception(errno);
+ if (elf_load(symbols) < 0) exception(errno);
+ if (r->entsize == 0 || r->size % r->entsize != 0) str_exception(ERR_INV_FORMAT, "Invalid sh_entsize");
+ p = (uint8_t *)r->data;
+ q = p + r->size;
+ while (p < q) {
+ unsigned n = (q - p) / r->entsize / 2;
+ uint8_t * x = p + n * r->entsize;
+ assert(x < q);
+ if (r->file->elf64) {
+ U8_T offs = *(U8_T *)x;
+ if (r->file->byte_swap) SWAP(offs);
+ if (s->file->type != ET_REL) offs -= s->addr;
+ if (offset > offs) {
+ p = x + r->entsize;
+ continue;
+ }
+ if (offset < offs) {
+ q = x;
+ continue;
+ }
+ }
+ else {
+ U4_T offs = *(U4_T *)x;
+ if (r->file->byte_swap) SWAP(offs);
+ if (s->file->type != ET_REL) offs -= (U4_T)s->addr;
+ if (offset > offs) {
+ p = x + r->entsize;
+ continue;
+ }
+ if (offset < offs) {
+ q = x;
+ continue;
+ }
+ }
+ relocate(x);
+ return;
+ }
+ }
+ }
+}
+
+#endif /* ENABLE_ELF */
diff --git a/agent/tcf/services/dwarfreloc.h b/agent/tcf/services/dwarfreloc.h
new file mode 100644
index 00000000..af8f3e2c
--- /dev/null
+++ b/agent/tcf/services/dwarfreloc.h
@@ -0,0 +1,35 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+
+/*
+ * This module implements ELF relocation records handling for reading DWARF debug information.
+ *
+ * Functions in this module use exceptions to report errors, see exceptions.h
+ */
+
+#ifndef D_dwarfreloc
+#define D_dwarfreloc
+
+#include <config.h>
+
+#if ENABLE_ELF
+
+#include <services/tcf_elf.h>
+
+extern void drl_relocate(ELF_Section * s, U8_T offset, void * buf, size_t size, ELF_Section ** dst);
+
+#endif /* ENABLE_ELF */
+
+#endif /* D_dwarfreloc */
diff --git a/agent/tcf/services/expressions.c b/agent/tcf/services/expressions.c
new file mode 100644
index 00000000..c58757bd
--- /dev/null
+++ b/agent/tcf/services/expressions.c
@@ -0,0 +1,2809 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2011 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
+ *******************************************************************************/
+
+/*
+ * Expression evaluation service.
+ *
+ * Extensions to regular C/C++ syntax:
+ * 1. Special characters in identifiers: $"X"
+ * where X is object name that can contain any characters.
+ * 2. Symbol IDs in expressions: ${X}
+ * where X is symbol ID as returned by symbols service.
+ */
+
+#include <config.h>
+
+#if SERVICE_Expressions
+
+#include <stdlib.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <assert.h>
+#include <string.h>
+#include <framework/myalloc.h>
+#include <framework/exceptions.h>
+#include <framework/json.h>
+#include <framework/cache.h>
+#include <framework/context.h>
+#include <services/symbols.h>
+#include <services/stacktrace.h>
+#include <services/expressions.h>
+#include <services/memoryservice.h>
+#include <services/registers.h>
+#include <main/test.h>
+
+#define STR_POOL_SIZE (64 * MEM_USAGE_FACTOR)
+
+struct StringValue {
+ struct StringValue * next;
+ char buf[1];
+};
+
+typedef struct StringValue StringValue;
+
+#define SY_LEQ 256
+#define SY_GEQ 257
+#define SY_EQU 258
+#define SY_NEQ 259
+#define SY_AND 260
+#define SY_OR 261
+#define SY_SHL 262
+#define SY_SHR 263
+#define SY_VAL 264
+#define SY_ID 265
+#define SY_REF 266
+#define SY_DEC 267
+#define SY_INC 268
+#define SY_A_SUB 269
+#define SY_A_ADD 270
+#define SY_A_SHL 271
+#define SY_A_SHR 272
+#define SY_A_OR 273
+#define SY_A_XOR 274
+#define SY_A_AND 275
+#define SY_A_MUL 276
+#define SY_A_DIV 277
+#define SY_A_MOD 278
+#define SY_SIZEOF 279
+#define SY_NAME 280
+#define SY_SCOPE 281
+
+#define MODE_NORMAL 0
+#define MODE_TYPE 1
+#define MODE_SKIP 2
+
+static char * text = NULL;
+static int text_pos = 0;
+static int text_len = 0;
+static int text_ch = 0;
+static int text_sy = 0;
+static Value text_val;
+
+/* Host endianness */
+static int big_endian = 0;
+
+static char str_pool[STR_POOL_SIZE];
+static int str_pool_cnt = 0;
+static StringValue * str_alloc_list = NULL;
+
+static Context * expression_context = NULL;
+static int expression_frame = STACK_NO_FRAME;
+static ContextAddress expression_addr = 0;
+
+#define MAX_ID_CALLBACKS 8
+static ExpressionIdentifierCallBack * id_callbacks[MAX_ID_CALLBACKS];
+static int id_callback_cnt = 0;
+
+static void * alloc_str(size_t size) {
+ if (str_pool_cnt + size <= STR_POOL_SIZE) {
+ char * s = str_pool + str_pool_cnt;
+ str_pool_cnt += size;
+ return s;
+ }
+ else {
+ StringValue * s = (StringValue *)loc_alloc(sizeof(StringValue) + size - 1);
+ s->next = str_alloc_list;
+ str_alloc_list = s;
+ return s->buf;
+ }
+}
+
+void set_value(Value * v, void * data, size_t size, int big_endian) {
+ v->sym = NULL;
+ v->reg = NULL;
+ v->remote = 0;
+ v->address = 0;
+ v->function = 0;
+ v->size = (ContextAddress)size;
+ v->big_endian = big_endian;
+ v->value = alloc_str(size);
+ if (data == NULL) memset(v->value, 0, size);
+ else memcpy(v->value, data, size);
+}
+
+static void set_int_value(Value * v, size_t size, uint64_t n) {
+ union {
+ uint8_t u8;
+ uint16_t u16;
+ uint32_t u32;
+ uint64_t u64;
+ } buf;
+ switch (size) {
+ case 1: buf.u8 = (uint8_t)n; break;
+ case 2: buf.u16 = (uint16_t)n; break;
+ case 4: buf.u32 = (uint32_t)n; break;
+ case 8: buf.u64 = n; break;
+ default: assert(0);
+ }
+ set_value(v, &buf, size, big_endian);
+}
+
+static void set_fp_value(Value * v, size_t size, double n) {
+ union {
+ float f;
+ double d;
+ } buf;
+ switch (size) {
+ case 4: buf.f = (float)n; break;
+ case 8: buf.d = n; break;
+ default: assert(0);
+ }
+ set_value(v, &buf, size, big_endian);
+}
+
+static void set_ctx_word_value(Value * v, ContextAddress data) {
+ set_int_value(v, context_word_size(expression_context), data);
+}
+
+static void set_string_value(Value * v, char * str) {
+ v->type_class = TYPE_CLASS_ARRAY;
+ if (str != NULL) set_value(v, str, strlen(str) + 1, 0);
+}
+
+static void error(int no, const char * fmt, ...) {
+ va_list ap;
+ char buf[256];
+ size_t l = 0;
+
+ va_start(ap, fmt);
+ l = snprintf(buf, sizeof(buf), "At col %d: ", text_pos);
+ vsnprintf(buf + l, sizeof(buf) - l, fmt, ap);
+ va_end(ap);
+ str_exception(no, buf);
+}
+
+static void next_ch(void) {
+ if (text_pos >= text_len) return;
+ text_ch = (unsigned char)text[text_pos++];
+}
+
+static int next_hex(void) {
+ int ch = text_ch;
+ next_ch();
+ if (ch >= '0' && ch <= '9') return ch - '0';
+ if (ch >= 'A' && ch <= 'F') return ch - 'A' + 10;
+ if (ch >= 'a' && ch <= 'f') return ch - 'a' + 10;
+ error(ERR_INV_EXPRESSION, "Invalid hexadecimal number");
+ return 0;
+}
+
+static int next_oct(void) {
+ int ch = text_ch;
+ next_ch();
+ if (ch >= '0' && ch <= '7') return ch - '0';
+ error(ERR_INV_EXPRESSION, "Invalid octal number");
+ return 0;
+}
+
+static int next_dec(void) {
+ int ch = text_ch;
+ next_ch();
+ if (ch >= '0' && ch <= '9') return ch - '0';
+ error(ERR_INV_EXPRESSION, "Invalid decimal number");
+ return 0;
+}
+
+static int next_char_val(void) {
+ int n = 0;
+ if (text_ch == '\\') {
+ next_ch();
+ switch (text_ch) {
+ case 'n' : n = '\n'; break;
+ case 't' : n = '\t'; break;
+ case 'v' : n = '\v'; break;
+ case 'b' : n = '\b'; break;
+ case 'r' : n = '\r'; break;
+ case 'f' : n = '\f'; break;
+ case 'a' : n = '\a'; break;
+ case '\\': n = '\\'; break;
+ case '\'': n = '\''; break;
+ case '"' : n = '"'; break;
+ case 'x' :
+ next_ch();
+ n = next_hex() << 8;
+ n |= next_hex() << 4;
+ n |= next_hex();
+ return n;
+ case '0' :
+ case '1' :
+ case '2' :
+ case '3' :
+ n = next_oct() << 6;
+ n |= next_oct() << 3;
+ n |= next_oct();
+ return n;
+ default :
+ n = text_ch;
+ break;
+ }
+ }
+ else {
+ n = text_ch;
+ }
+ next_ch();
+ return n;
+}
+
+static void set_string_text_val(int pos, int len, int in_quotes) {
+ int cnt = 0;
+ memset(&text_val, 0, sizeof(text_val));
+ text_val.type_class = TYPE_CLASS_ARRAY;
+ text_val.size = len + 1;
+ text_val.value = alloc_str((size_t)text_val.size);
+ text_val.constant = 1;
+ text_pos = pos - 1;
+ next_ch();
+ if (in_quotes) {
+ while (cnt < len) {
+ ((char *)text_val.value)[cnt++] = (char)next_char_val();
+ }
+ }
+ else {
+ while (cnt < len) {
+ ((char *)text_val.value)[cnt++] = (char)text_ch;
+ next_ch();
+ }
+ }
+ ((char *)text_val.value)[cnt] = 0;
+}
+
+static int is_name_character(int ch) {
+ if (ch >= 'A' && ch <= 'Z') return 1;
+ if (ch >= 'a' && ch <= 'z') return 1;
+ if (ch >= '0' && ch <= '9') return 1;
+ if (ch == '_') return 1;
+ if (ch == '$') return 1;
+ if (ch == '@') return 1;
+ return 0;
+}
+
+static void next_sy(void) {
+ for (;;) {
+ int ch = text_ch;
+ next_ch();
+ switch (ch) {
+ case 0:
+ text_sy = 0;
+ return;
+ case ' ':
+ case '\r':
+ case '\n':
+ case '\t':
+ continue;
+ case '(':
+ case ')':
+ case '{':
+ case '}':
+ case '~':
+ case '[':
+ case ']':
+ case ';':
+ case '?':
+ case ',':
+ case '.':
+ text_sy = ch;
+ return;
+ case ':':
+ if (text_ch == ':') {
+ next_ch();
+ text_sy = SY_SCOPE;
+ return;
+ }
+ text_sy = ch;
+ return;
+ case '-':
+ if (text_ch == '>') {
+ next_ch();
+ text_sy = SY_REF;
+ return;
+ }
+ if (text_ch == '-') {
+ next_ch();
+ text_sy = SY_DEC;
+ return;
+ }
+ if (text_ch == '=') {
+ next_ch();
+ text_sy = SY_A_SUB;
+ return;
+ }
+ text_sy = ch;
+ return;
+ case '+':
+ if (text_ch == '+') {
+ next_ch();
+ text_sy = SY_INC;
+ return;
+ }
+ if (text_ch == '=') {
+ next_ch();
+ text_sy = SY_A_ADD;
+ return;
+ }
+ text_sy = ch;
+ return;
+ case '<':
+ if (text_ch == '<') {
+ next_ch();
+ if (text_ch == '=') {
+ next_ch();
+ text_sy = SY_A_SHL;
+ return;
+ }
+ text_sy = SY_SHL;
+ return;
+ }
+ if (text_ch == '=') {
+ next_ch();
+ text_sy = SY_LEQ;
+ return;
+ }
+ text_sy = ch;
+ return;
+ case '>':
+ if (text_ch == '>') {
+ next_ch();
+ if (text_ch == '=') {
+ next_ch();
+ text_sy = SY_A_SHR;
+ return;
+ }
+ text_sy = SY_SHR;
+ return;
+ }
+ if (text_ch == '=') {
+ next_ch();
+ text_sy = SY_GEQ;
+ return;
+ }
+ text_sy = ch;
+ return;
+ case '=':
+ if (text_ch == '=') {
+ next_ch();
+ text_sy = SY_EQU;
+ return;
+ }
+ text_sy = ch;
+ return;
+ case '!':
+ if (text_ch == '=') {
+ next_ch();
+ text_sy = SY_NEQ;
+ return;
+ }
+ text_sy = ch;
+ return;
+ case '&':
+ if (text_ch == '&') {
+ next_ch();
+ text_sy = SY_AND;
+ return;
+ }
+ if (text_ch == '=') {
+ next_ch();
+ text_sy = SY_A_AND;
+ return;
+ }
+ text_sy = ch;
+ return;
+ case '|':
+ if (text_ch == '|') {
+ next_ch();
+ text_sy = SY_OR;
+ return;
+ }
+ if (text_ch == '=') {
+ next_ch();
+ text_sy = SY_A_OR;
+ return;
+ }
+ text_sy = ch;
+ return;
+ case '*':
+ if (text_ch == '=') {
+ next_ch();
+ text_sy = SY_A_MUL;
+ return;
+ }
+ text_sy = ch;
+ return;
+ case '/':
+ if (text_ch == '|') {
+ next_ch();
+ text_sy = SY_A_DIV;
+ return;
+ }
+ text_sy = ch;
+ return;
+ case '%':
+ if (text_ch == '|') {
+ next_ch();
+ text_sy = SY_A_MOD;
+ return;
+ }
+ text_sy = ch;
+ return;
+ case '^':
+ if (text_ch == '=') {
+ next_ch();
+ text_sy = SY_A_XOR;
+ return;
+ }
+ text_sy = ch;
+ return;
+ case '\'':
+ memset(&text_val, 0, sizeof(text_val));
+ text_val.type_class = TYPE_CLASS_INTEGER;
+ set_int_value(&text_val, sizeof(uint16_t), next_char_val());
+ text_val.constant = 1;
+ if (text_ch != '\'') error(ERR_INV_EXPRESSION, "Missing 'single quote'");
+ next_ch();
+ text_sy = SY_VAL;
+ return;
+ case '"':
+ {
+ int len = 0;
+ int pos = text_pos;
+ while (text_ch != '"') {
+ next_char_val();
+ len++;
+ }
+ set_string_text_val(pos, len, 1);
+ text_sy = SY_VAL;
+ next_ch();
+ }
+ return;
+ case '0':
+ if (text_ch == 'x') {
+ uint64_t value = 0;
+ next_ch();
+ while ((text_ch >= '0' && text_ch <= '9') ||
+ (text_ch >= 'A' && text_ch <= 'F') ||
+ (text_ch >= 'a' && text_ch <= 'f')) {
+ value = (value << 4) | next_hex();
+ }
+ memset(&text_val, 0, sizeof(text_val));
+ text_val.type_class = TYPE_CLASS_CARDINAL;
+ set_int_value(&text_val, sizeof(uint64_t), value);
+ text_val.constant = 1;
+ }
+ else {
+ int64_t value = 0;
+ while (text_ch >= '0' && text_ch <= '7') {
+ value = (value << 3) | next_oct();
+ }
+ memset(&text_val, 0, sizeof(text_val));
+ text_val.type_class = TYPE_CLASS_INTEGER;
+ set_int_value(&text_val, sizeof(int64_t), value);
+ text_val.constant = 1;
+ }
+ text_sy = SY_VAL;
+ return;
+ default:
+ if (ch >= '0' && ch <= '9') {
+ int pos = text_pos - 2;
+ int64_t value = ch - '0';
+ while (text_ch >= '0' && text_ch <= '9') {
+ value = (value * 10) + next_dec();
+ }
+ memset(&text_val, 0, sizeof(text_val));
+ if (text_ch == '.') {
+ char * end = NULL;
+ double x = strtod(text + pos, &end);
+ text_pos = end - text;
+ next_ch();
+ text_val.type_class = TYPE_CLASS_REAL;
+ set_fp_value(&text_val, sizeof(double), x);
+ }
+ else {
+ text_val.type_class = TYPE_CLASS_INTEGER;
+ set_int_value(&text_val, sizeof(int64_t), value);
+ }
+ text_val.constant = 1;
+ text_sy = SY_VAL;
+ return;
+ }
+ if (ch == '$') {
+ if (text_ch == '"') {
+ int len = 0;
+ int pos = text_pos + 1;
+ next_char_val();
+ while (text_ch != '"') {
+ next_char_val();
+ len++;
+ }
+ set_string_text_val(pos, len, 1);
+ text_sy = SY_NAME;
+ next_ch();
+ return;
+ }
+ if (text_ch == '{') {
+ int len = 0;
+ int pos = text_pos + 1;
+ next_ch();
+ while (text_ch != '}') {
+ next_ch();
+ len++;
+ }
+ set_string_text_val(pos, len, 0);
+ text_sy = SY_ID;
+ next_ch();
+ return;
+ }
+ }
+ if (is_name_character(ch)) {
+ int len = 1;
+ int pos = text_pos - 1;
+ while (is_name_character(text_ch)) {
+ next_ch();
+ len++;
+ }
+ set_string_text_val(pos, len, 0);
+ if (strcmp((const char *)text_val.value, "sizeof") == 0) text_sy = (int)SY_SIZEOF;
+ else text_sy = SY_NAME;
+ return;
+ }
+ error(ERR_INV_EXPRESSION, "Illegal character");
+ break;
+ }
+ }
+}
+
+static void reg2value(Context * ctx, int frame, RegisterDefinition * def, Value * v) {
+ memset(v, 0, sizeof(Value));
+ set_value(v, NULL, def->size, def->big_endian);
+ v->type_class = def->fp_value ? TYPE_CLASS_REAL : TYPE_CLASS_CARDINAL;
+ v->reg = def;
+ if (frame == STACK_TOP_FRAME) {
+ if (context_read_reg(ctx, def, 0, def->size, v->value) < 0) exception(errno);
+ }
+ else {
+ StackFrame * info = NULL;
+ if (get_frame_info(ctx, frame, &info) < 0) exception(errno);
+ if (read_reg_bytes(info, def, 0, def->size, (uint8_t *)v->value) < 0) exception(errno);
+ }
+}
+
+#if ENABLE_Symbols
+static void set_value_endianness(Value * v, Symbol * sym, Symbol * type) {
+ SYM_FLAGS flags = 0;
+ if (sym != NULL && get_symbol_flags(sym, &flags) < 0) {
+ error(errno, "Cannot retrieve symbol flags");
+ }
+ if (flags & SYM_FLAG_BIG_ENDIAN) v->big_endian = 1;
+ else if (flags & SYM_FLAG_LITTLE_ENDIAN) v->big_endian = 0;
+ else {
+ if (type != NULL && get_symbol_flags(type, &flags) < 0) {
+ error(errno, "Cannot retrieve symbol flags");
+ }
+ if (flags & SYM_FLAG_BIG_ENDIAN) v->big_endian = 1;
+ else if (flags & SYM_FLAG_LITTLE_ENDIAN) v->big_endian = 0;
+ else v->big_endian = expression_context->big_endian;
+ }
+}
+
+/* Note: sym2value() does NOT set v->size if v->sym != NULL */
+static int sym2value(Symbol * sym, Value * v) {
+ int sym_class = 0;
+ memset(v, 0, sizeof(Value));
+ if (get_symbol_class(sym, &sym_class) < 0) {
+ error(errno, "Cannot retrieve symbol class");
+ }
+ if (get_symbol_type(sym, &v->type) < 0) {
+ error(errno, "Cannot retrieve symbol type");
+ }
+ if (get_symbol_type_class(sym, &v->type_class) < 0) {
+ error(errno, "Cannot retrieve symbol type class");
+ }
+ switch (sym_class) {
+ case SYM_CLASS_VALUE:
+ {
+ int endianness = 0;
+ size_t size = 0;
+ void * value = NULL;
+ if (get_symbol_value(sym, &value, &size, &endianness) < 0) {
+ error(errno, "Cannot retrieve symbol value");
+ }
+ v->big_endian = endianness;
+ v->constant = 1;
+ v->size = size;
+ if (value != NULL) {
+ v->value = alloc_str(size);
+ memcpy(v->value, value, size);
+ }
+ }
+ break;
+ case SYM_CLASS_REFERENCE:
+ if (get_symbol_address(sym, &v->address) < 0) {
+ int endianness = 0;
+ size_t size = 0;
+ void * value = NULL;
+ int frame = 0;
+ Context * ctx = NULL;
+ RegisterDefinition * reg = NULL;
+ if (get_symbol_value(sym, &value, &size, &endianness) < 0) {
+ error(errno, "Cannot retrieve symbol value");
+ }
+ if (get_symbol_register(sym, &ctx, &frame, &reg) == 0 &&
+ ctx == expression_context && frame == expression_frame) {
+ v->reg = reg;
+ }
+ v->big_endian = endianness;
+ v->size = size;
+ if (value != NULL) {
+ v->value = alloc_str(size);
+ memcpy(v->value, value, size);
+ }
+ }
+ else {
+ set_value_endianness(v, sym, v->type);
+ v->sym = sym;
+ v->remote = 1;
+ }
+ break;
+ case SYM_CLASS_FUNCTION:
+ {
+ ContextAddress word = 0;
+ v->type_class = TYPE_CLASS_CARDINAL;
+ if (v->type != NULL) get_array_symbol(v->type, 0, &v->type);
+ if (get_symbol_address(sym, &word) < 0) {
+ error(errno, "Cannot retrieve symbol address");
+ }
+ set_ctx_word_value(v, word);
+ v->function = 1;
+ }
+ break;
+ default:
+ v->type = sym;
+ break;
+ }
+ return sym_class;
+}
+#endif
+
+static int identifier(Value * scope, char * name, Value * v) {
+ int i;
+ memset(v, 0, sizeof(Value));
+ if (scope == NULL) {
+ for (i = 0; i < id_callback_cnt; i++) {
+ if (id_callbacks[i](expression_context, expression_frame, name, v)) return SYM_CLASS_VALUE;
+ }
+ if (expression_context == NULL) {
+ exception(ERR_INV_CONTEXT);
+ }
+ if (name[0] == '$') {
+ RegisterDefinition * def = get_reg_definitions(expression_context);
+ if (def != NULL) {
+ while (def->name != NULL) {
+ if (strcmp(name + 1, def->name) == 0) {
+ reg2value(expression_context, expression_frame, def, v);
+ return SYM_CLASS_REFERENCE;
+ }
+ def++;
+ }
+ }
+ }
+ if (strcmp(name, "$thread") == 0) {
+ set_string_value(v, expression_context->id);
+ v->constant = 1;
+ return SYM_CLASS_VALUE;
+ }
+ }
+#if ENABLE_Symbols
+ {
+ Symbol * sym = NULL;
+ int n = scope != NULL ?
+ find_symbol_in_scope(expression_context, expression_frame, expression_addr, scope->type, name, &sym) :
+ find_symbol_by_name(expression_context, expression_frame, expression_addr, name, &sym);
+
+ if (n < 0) {
+ if (get_error_code(errno) != ERR_SYM_NOT_FOUND) error(errno, "Cannot read symbol data");
+ }
+ else {
+ return sym2value(sym, v);
+ }
+ }
+#elif ENABLE_RCBP_TEST
+ {
+ void * ptr = NULL;
+ int cls = 0;
+ if (find_test_symbol(expression_context, name, &ptr, &cls) >= 0) {
+ v->type_class = TYPE_CLASS_CARDINAL;
+ set_ctx_word_value(v, (ContextAddress)ptr);
+ return cls;
+ }
+ }
+#endif
+ return -1;
+}
+
+static int64_t to_int(int mode, Value * v);
+#define TYPE_EXPR_LENGTH 64
+
+static int type_expression(int mode, int * buf) {
+ int i = 0;
+ int pos = 0;
+ int expr_buf[TYPE_EXPR_LENGTH];
+ int expr_len = 0;
+ while (text_sy == '*') {
+ next_sy();
+ if (pos >= TYPE_EXPR_LENGTH) error(ERR_BUFFER_OVERFLOW, "Type expression is too long");
+ buf[pos++] = 1;
+ }
+ if (text_sy == '(') {
+ next_sy();
+ expr_len = type_expression(mode, expr_buf);
+ if (text_sy != ')') error(ERR_INV_EXPRESSION, "')' expected");
+ next_sy();
+ }
+ while (text_sy == '[') {
+ next_sy();
+ if (text_sy != SY_VAL) error(ERR_INV_EXPRESSION, "Number expected");
+ if (pos >= TYPE_EXPR_LENGTH) error(ERR_BUFFER_OVERFLOW, "Type expression is too long");
+ buf[pos] = (int)to_int(mode, &text_val);
+ if (mode == MODE_NORMAL && buf[pos] < 1) error(ERR_INV_EXPRESSION, "Positive number expected");
+ pos++;
+ next_sy();
+ if (text_sy != ']') error(ERR_INV_EXPRESSION, "']' expected");
+ next_sy();
+ }
+ for (i = 0; i < expr_len; i++) {
+ if (pos >= TYPE_EXPR_LENGTH) error(ERR_BUFFER_OVERFLOW, "Type expression is too long");
+ buf[pos++] = expr_buf[i];
+ }
+ return pos;
+}
+
+static int type_name(int mode, Symbol ** type) {
+ Value v;
+ int expr_buf[TYPE_EXPR_LENGTH];
+ int expr_len = 0;
+ char name[256];
+ int sym_class;
+ int is_struct = 0;
+ int is_class = 0;
+ int name_cnt = 0;
+
+ if (text_sy == SY_NAME) {
+ if (strcmp((const char *)(text_val.value), "struct") == 0) {
+ is_struct = 1;
+ next_sy();
+ }
+ else if (strcmp((const char *)(text_val.value), "class") == 0) {
+ is_class = 1;
+ next_sy();
+ }
+ }
+
+ if (text_sy != SY_NAME) return 0;
+ name[0] = 0;
+ do {
+ if (strlen((const char *)(text_val.value)) + strlen(name) >= sizeof(name) - 1) {
+ error(ERR_BUFFER_OVERFLOW, "Type name is too long");
+ }
+ if (name[0]) strcat(name, " ");
+ strcat(name, (const char *)(text_val.value));
+ name_cnt++;
+ next_sy();
+ }
+ while (text_sy == SY_NAME);
+ sym_class = identifier(NULL, name, &v);
+ if (sym_class != SYM_CLASS_TYPE) {
+ if (is_struct || is_class) {
+ error(ERR_INV_EXPRESSION, "Type '%s' not found", name);
+ }
+ return 0;
+ }
+ expr_len = type_expression(mode, expr_buf);
+ if (mode != MODE_SKIP) {
+ int i;
+ for (i = 0; i < expr_len; i++) {
+#if ENABLE_Symbols
+ if (expr_buf[i] == 1) {
+ if (get_array_symbol(v.type, 0, &v.type)) {
+ error(errno, "Cannot create pointer type");
+ }
+ }
+ else {
+ if (get_array_symbol(v.type, expr_buf[i], &v.type)) {
+ error(errno, "Cannot create array type");
+ }
+ }
+#else
+ v.type = NULL;
+#endif
+ }
+ }
+ *type = v.type;
+ return 1;
+}
+
+static void load_value(Value * v) {
+ v->sym = NULL;
+ v->reg = NULL;
+ if (v->remote) {
+ size_t size = (size_t)v->size;
+ void * buf = alloc_str(size);
+ assert(!v->constant);
+ if (context_read_mem(expression_context, v->address, buf, size) < 0) {
+ error(errno, "Can't read variable value");
+ }
+ v->value = buf;
+ v->remote = 0;
+ }
+}
+
+static int is_number(Value * v) {
+ switch (v->type_class) {
+ case TYPE_CLASS_INTEGER:
+ case TYPE_CLASS_CARDINAL:
+ case TYPE_CLASS_REAL:
+ case TYPE_CLASS_ENUMERATION:
+ return 1;
+ }
+ return 0;
+}
+
+static int is_whole_number(Value * v) {
+ switch (v->type_class) {
+ case TYPE_CLASS_INTEGER:
+ case TYPE_CLASS_CARDINAL:
+ case TYPE_CLASS_ENUMERATION:
+ return 1;
+ }
+ return 0;
+}
+
+static void to_host_endianness(Value * v) {
+ assert(v->type_class != TYPE_CLASS_COMPOSITE);
+ assert(v->type_class != TYPE_CLASS_ARRAY);
+ assert(!v->remote);
+ if (v->big_endian != big_endian) {
+ size_t i = 0;
+ size_t n = (size_t)v->size;
+ uint8_t * buf = (uint8_t *)alloc_str(n);
+ for (i = 0; i < n; i++) {
+ buf[i] = ((uint8_t *)v->value)[n - i - 1];
+ }
+ v->value = buf;
+ v->big_endian = big_endian;
+ v->sym = NULL;
+ v->reg = NULL;
+ }
+}
+
+static int64_t to_int(int mode, Value * v) {
+ if (mode != MODE_NORMAL) {
+ v->sym = NULL;
+ v->reg = NULL;
+ if (v->remote) {
+ v->value = alloc_str((size_t)v->size);
+ v->remote = 0;
+ }
+ return 0;
+ }
+
+ if (v->type_class == TYPE_CLASS_POINTER) {
+ load_value(v);
+ to_host_endianness(v);
+ switch (v->size) {
+ case 1: return *(uint8_t *)v->value;
+ case 2: return *(uint16_t *)v->value;
+ case 4: return *(uint32_t *)v->value;
+ case 8: return *(uint64_t *)v->value;
+ }
+ }
+ if (is_number(v)) {
+ load_value(v);
+ to_host_endianness(v);
+
+ if (v->type_class == TYPE_CLASS_REAL) {
+ switch (v->size) {
+ case 4: return (int64_t)*(float *)v->value;
+ case 8: return (int64_t)*(double *)v->value;
+ }
+ }
+ else if (v->type_class == TYPE_CLASS_CARDINAL) {
+ switch (v->size) {
+ case 1: return (int64_t)*(uint8_t *)v->value;
+ case 2: return (int64_t)*(uint16_t *)v->value;
+ case 4: return (int64_t)*(uint32_t *)v->value;
+ case 8: return (int64_t)*(uint64_t *)v->value;
+ }
+ }
+ else {
+ switch (v->size) {
+ case 1: return *(int8_t *)v->value;
+ case 2: return *(int16_t *)v->value;
+ case 4: return *(int32_t *)v->value;
+ case 8: return *(int64_t *)v->value;
+ }
+ }
+ }
+
+ error(ERR_INV_EXPRESSION, "Operation is not applicable for the value type");
+ return 0;
+}
+
+static uint64_t to_uns(int mode, Value * v) {
+ if (mode != MODE_NORMAL) {
+ v->sym = NULL;
+ v->reg = NULL;
+ if (v->remote) {
+ v->value = alloc_str((size_t)v->size);
+ v->remote = 0;
+ }
+ return 0;
+ }
+
+ if (v->type_class == TYPE_CLASS_ARRAY && v->remote) {
+ return (uint64_t)v->address;
+ }
+ if (v->type_class == TYPE_CLASS_POINTER) {
+ load_value(v);
+ to_host_endianness(v);
+ switch (v->size) {
+ case 1: return *(uint8_t *)v->value;
+ case 2: return *(uint16_t *)v->value;
+ case 4: return *(uint32_t *)v->value;
+ case 8: return *(uint64_t *)v->value;
+ }
+ }
+ if (is_number(v)) {
+ load_value(v);
+ to_host_endianness(v);
+
+ if (v->type_class == TYPE_CLASS_REAL) {
+ switch (v->size) {
+ case 4: return (uint64_t)*(float *)v->value;
+ case 8: return (uint64_t)*(double *)v->value;
+ }
+ }
+ else if (v->type_class == TYPE_CLASS_CARDINAL) {
+ switch (v->size) {
+ case 1: return *(uint8_t *)v->value;
+ case 2: return *(uint16_t *)v->value;
+ case 4: return *(uint32_t *)v->value;
+ case 8: return *(uint64_t *)v->value;
+ }
+ }
+ else {
+ switch (v->size) {
+ case 1: return (uint64_t)*(int8_t *)v->value;
+ case 2: return (uint64_t)*(int16_t *)v->value;
+ case 4: return (uint64_t)*(int32_t *)v->value;
+ case 8: return (uint64_t)*(int64_t *)v->value;
+ }
+ }
+ }
+
+ error(ERR_INV_EXPRESSION, "Operation is not applicable for the value type");
+ return 0;
+}
+
+static double to_double(int mode, Value * v) {
+ if (mode != MODE_NORMAL) {
+ v->sym = NULL;
+ v->reg = NULL;
+ if (v->remote) {
+ v->value = alloc_str((size_t)v->size);
+ v->remote = 0;
+ }
+ return 0;
+ }
+
+ if (is_number(v)) {
+ load_value(v);
+ to_host_endianness(v);
+
+ if (v->type_class == TYPE_CLASS_REAL) {
+ switch (v->size) {
+ case 4: return *(float *)v->value;
+ case 8: return *(double *)v->value;
+ }
+ }
+ else if (v->type_class == TYPE_CLASS_CARDINAL) {
+ switch (v->size) {
+ case 1: return (double)*(uint8_t *)v->value;
+ case 2: return (double)*(uint16_t *)v->value;
+ case 4: return (double)*(uint32_t *)v->value;
+ case 8: return (double)*(uint64_t *)v->value;
+ }
+ }
+ else {
+ switch (v->size) {
+ case 1: return (double)*(int8_t *)v->value;
+ case 2: return (double)*(int16_t *)v->value;
+ case 4: return (double)*(int32_t *)v->value;
+ case 8: return (double)*(int64_t *)v->value;
+ }
+ }
+ }
+
+ error(ERR_INV_EXPRESSION, "Operation is not applicable for the value type");
+ return 0;
+}
+
+static int to_boolean(int mode, Value * v) {
+ return to_int(mode, v) != 0;
+}
+
+static void expression(int mode, Value * v);
+
+static int qualified_name(int mode, Value * scope, Value * v) {
+ Value x;
+ int sym_class = 0;
+ for (;;) {
+ if (text_sy != SY_NAME) error(ERR_INV_EXPRESSION, "Identifier expected");
+ if (mode != MODE_SKIP) {
+ int sym_class = identifier(scope, (char *)text_val.value, v);
+ if (sym_class < 0) error(ERR_INV_EXPRESSION, "Undefined identifier '%s'", text_val.value);
+ }
+ else {
+ memset(v, 0, sizeof(Value));
+ }
+ next_sy();
+ if (text_sy != SY_SCOPE) break;
+ next_sy();
+ scope = &x;
+ x = *v;
+ }
+ return sym_class;
+}
+
+static void primary_expression(int mode, Value * v) {
+ if (text_sy == '(') {
+ next_sy();
+ expression(mode, v);
+ if (text_sy != ')') error(ERR_INV_EXPRESSION, "Missing ')'");
+ next_sy();
+ }
+ else if (text_sy == SY_VAL) {
+ if (mode != MODE_SKIP) *v = text_val;
+ else memset(v, 0, sizeof(Value));
+ next_sy();
+ }
+ else if (text_sy == SY_SCOPE) {
+ Value x;
+ next_sy();
+ memset(&x, 0, sizeof(x));
+ if (qualified_name(mode, &x, v) == SYM_CLASS_TYPE)
+ error(ERR_INV_EXPRESSION, "Illegal usage of a type in expression");
+ }
+ else if (text_sy == SY_NAME) {
+ if (qualified_name(mode, NULL, v) == SYM_CLASS_TYPE)
+ error(ERR_INV_EXPRESSION, "Illegal usage of a type in expression");
+ }
+ else if (text_sy == SY_ID) {
+ if (mode != MODE_SKIP) {
+ int ok = 0;
+ const char * id = (char *)text_val.value;
+ {
+ Context * ctx = NULL;
+ int frame = STACK_NO_FRAME;
+ RegisterDefinition * def = NULL;
+ if (id2register(id, &ctx, &frame, &def) >= 0) {
+ if (frame == STACK_TOP_FRAME) frame = expression_frame;
+ reg2value(ctx, frame, def, v);
+ ok = 1;
+ }
+ }
+#if ENABLE_Symbols
+ if (!ok) {
+ Symbol * sym = NULL;
+ if (id2symbol(id, &sym) >= 0) {
+ int sym_class = sym2value(sym, v);
+ if (sym_class == SYM_CLASS_TYPE) error(ERR_INV_EXPRESSION, "Illegal usage of type '%s'", id);
+ ok = 1;
+ }
+ }
+#endif
+ if (!ok) error(ERR_INV_EXPRESSION, "Symbol not found: %s", id);
+ }
+ next_sy();
+ }
+ else {
+ error(ERR_INV_EXPRESSION, "Syntax error");
+ }
+}
+
+static void op_deref(int mode, Value * v) {
+#if ENABLE_Symbols
+ Symbol * type = NULL;
+ if (mode == MODE_SKIP) return;
+ if (v->type_class != TYPE_CLASS_ARRAY && v->type_class != TYPE_CLASS_POINTER) {
+ error(ERR_INV_EXPRESSION, "Array or pointer type expected");
+ }
+ if (get_symbol_base_type(v->type, &type) < 0) {
+ error(errno, "Cannot retrieve symbol type");
+ }
+ if (v->type_class == TYPE_CLASS_POINTER) {
+ if (v->sym != NULL && v->size == 0 && get_symbol_size(v->sym, &v->size) < 0) {
+ error(errno, "Cannot retrieve symbol size");
+ }
+ v->address = (ContextAddress)to_uns(mode, v);
+ v->remote = 1;
+ v->constant = 0;
+ v->value = NULL;
+ set_value_endianness(v, NULL, type);
+ }
+ v->type = type;
+ if (get_symbol_type_class(v->type, &v->type_class) < 0) {
+ error(errno, "Cannot retrieve symbol type class");
+ }
+ if (get_symbol_size(v->type, &v->size) < 0) {
+ error(errno, "Cannot retrieve symbol size");
+ }
+#else
+ error(ERR_UNSUPPORTED, "Symbols service not available");
+#endif
+}
+
+#if ENABLE_Symbols
+static void find_field(Symbol * sym, ContextAddress offs, const char * name, Symbol ** res, ContextAddress * res_offs) {
+ Symbol ** children = NULL;
+ Symbol ** inheritance = NULL;
+ int count = 0;
+ int h = 0;
+ int i;
+
+ if (get_symbol_children(sym, &children, &count) < 0) {
+ error(errno, "Cannot retrieve field list");
+ }
+ for (i = 0; i < count; i++) {
+ char * s = NULL;
+ if (get_symbol_name(children[i], &s) < 0) {
+ error(errno, "Cannot retrieve field name");
+ }
+ if (s == NULL) {
+ if (inheritance == NULL) inheritance = (Symbol **)alloc_str(sizeof(Symbol *) * count);
+ inheritance[h++] = children[i];
+ }
+ else if (strcmp(s, name) == 0) {
+ *res = children[i];
+ *res_offs = offs;
+ return;
+ }
+ }
+ for (i = 0; i < h; i++) {
+ ContextAddress x = 0;
+ if (get_symbol_offset(inheritance[i], &x) < 0) {
+ error(errno, "Cannot retrieve field offset");
+ }
+ find_field(inheritance[i], offs + x, name, res, res_offs);
+ if (*res != NULL) return;
+ }
+}
+#endif
+
+static void op_field(int mode, Value * v) {
+ char * id = NULL;
+ char * name = NULL;
+ if (text_sy == SY_ID) id = (char *)text_val.value;
+ else if (text_sy == SY_NAME) name = (char *)text_val.value;
+ else error(ERR_INV_EXPRESSION, "Field name expected");
+ next_sy();
+ if (mode == MODE_SKIP) return;
+ if (v->type_class == TYPE_CLASS_COMPOSITE) {
+#if ENABLE_Symbols
+ Symbol * sym = NULL;
+ int sym_class = 0;
+ ContextAddress size = 0;
+ ContextAddress offs = 0;
+
+ if (id != NULL) {
+ if (id2symbol(id, &sym) < 0) error(errno, "Invalid field ID");
+ }
+ else {
+ find_field(v->type, 0, name, &sym, &offs);
+ }
+ if (sym == NULL) {
+ error(ERR_SYM_NOT_FOUND, "Symbol not found");
+ }
+ if (get_symbol_class(sym, &sym_class) < 0) {
+ error(errno, "Cannot retrieve symbol class");
+ }
+ if (sym_class == SYM_CLASS_FUNCTION) {
+ ContextAddress word = 0;
+ v->type_class = TYPE_CLASS_CARDINAL;
+ get_symbol_type(sym, &v->type);
+ if (v->type != NULL) get_array_symbol(v->type, 0, &v->type);
+ if (get_symbol_address(sym, &word) < 0) {
+ error(errno, "Cannot retrieve symbol address");
+ }
+ set_ctx_word_value(v, word);
+ v->function = 1;
+ }
+ else {
+ ContextAddress x = 0;
+ if (sym_class != SYM_CLASS_REFERENCE) {
+ error(ERR_UNSUPPORTED, "Invalid symbol class");
+ }
+ if (get_symbol_size(sym, &size) < 0) {
+ error(errno, "Cannot retrieve field size");
+ }
+ if (get_symbol_offset(sym, &x) < 0) {
+ error(errno, "Cannot retrieve field offset");
+ }
+ offs += x;
+ if (v->sym != NULL && v->size == 0 && get_symbol_size(v->sym, &v->size) < 0) {
+ error(errno, "Cannot retrieve symbol size");
+ }
+ if (offs + size > v->size) {
+ error(ERR_INV_EXPRESSION, "Invalid field offset and/or size");
+ }
+ if (v->remote) {
+ if (mode != MODE_TYPE) v->address += offs;
+ }
+ else {
+ v->value = (uint8_t *)v->value + offs;
+ }
+ v->sym = NULL;
+ v->reg = NULL;
+ v->size = size;
+ if (get_symbol_type(sym, &v->type) < 0) {
+ error(errno, "Cannot retrieve symbol type");
+ }
+ if (get_symbol_type_class(sym, &v->type_class) < 0) {
+ error(errno, "Cannot retrieve symbol type class");
+ }
+ set_value_endianness(v, sym, v->type);
+ }
+#else
+ error(ERR_UNSUPPORTED, "Symbols service not available");
+#endif
+ }
+ else if (v->reg != NULL) {
+ if (id != NULL) {
+ Context * ctx = NULL;
+ int frame = STACK_NO_FRAME;
+ RegisterDefinition * def = NULL;
+ if (id2register(id, &ctx, &frame, &def) < 0) exception(errno);
+ if (frame == STACK_TOP_FRAME) frame = expression_frame;
+ reg2value(ctx, frame, def, v);
+ }
+ else {
+ RegisterDefinition * def = get_reg_definitions(expression_context);
+ if (def != NULL) {
+ while (def->name != NULL) {
+ if (def->parent == v->reg && strcmp(name, def->name) == 0) {
+ reg2value(expression_context, expression_frame, def, v);
+ return;
+ }
+ def++;
+ }
+ }
+ error(ERR_INV_EXPRESSION, "Unknown register; %s", name);
+ }
+ }
+ else {
+ error(ERR_INV_EXPRESSION, "Composite type expected");
+ }
+}
+
+static void op_index(int mode, Value * v) {
+#if ENABLE_Symbols
+ Value i;
+ int64_t lower_bound = 0;
+ ContextAddress offs = 0;
+ ContextAddress size = 0;
+ Symbol * type = NULL;
+
+ expression(mode, &i);
+ if (mode == MODE_SKIP) return;
+
+ if (v->type_class != TYPE_CLASS_ARRAY && v->type_class != TYPE_CLASS_POINTER) {
+ error(ERR_INV_EXPRESSION, "Array or pointer expected");
+ }
+ if (v->type == NULL) {
+ error(ERR_INV_EXPRESSION, "Value type is unknown");
+ }
+ if (get_symbol_base_type(v->type, &type) < 0) {
+ error(errno, "Cannot get array element type");
+ }
+ if (v->type_class == TYPE_CLASS_POINTER) {
+ v->address = (ContextAddress)to_uns(mode, v);
+ v->remote = 1;
+ v->constant = 0;
+ v->value = NULL;
+ set_value_endianness(v, NULL, type);
+ }
+ if (get_symbol_size(type, &size) < 0) {
+ error(errno, "Cannot get array element size");
+ }
+ if (get_symbol_lower_bound(v->type, &lower_bound) < 0) {
+ error(errno, "Cannot get array lower bound");
+ }
+ offs = (ContextAddress)(to_int(mode, &i) - lower_bound) * size;
+ if (v->sym != NULL && v->size == 0 && get_symbol_size(v->sym, &v->size) < 0) {
+ error(errno, "Cannot retrieve symbol size");
+ }
+ if (v->type_class == TYPE_CLASS_ARRAY && offs + size > v->size) {
+ error(ERR_INV_EXPRESSION, "Invalid index");
+ }
+ if (v->remote) {
+ v->address += offs;
+ }
+ else {
+ v->value = (char *)v->value + offs;
+ }
+ v->sym = NULL;
+ v->reg = NULL;
+ v->size = size;
+ v->type = type;
+ if (get_symbol_type_class(type, &v->type_class) < 0) {
+ error(errno, "Cannot retrieve symbol type class");
+ }
+#else
+ error(ERR_UNSUPPORTED, "Symbols service not available");
+#endif
+}
+
+static void op_addr(int mode, Value * v) {
+ if (mode == MODE_SKIP) return;
+ if (v->function) {
+ v->type_class = TYPE_CLASS_POINTER;
+ v->function = 0;
+ }
+ else {
+ if (!v->remote) error(ERR_INV_EXPRESSION, "Invalid '&': value has no address");
+ set_ctx_word_value(v, v->address);
+ v->type_class = TYPE_CLASS_POINTER;
+ v->constant = 0;
+#if ENABLE_Symbols
+ if (v->type != NULL) {
+ if (get_array_symbol(v->type, 0, &v->type)) {
+ error(errno, "Cannot get pointer type");
+ }
+ }
+#else
+ v->type = NULL;
+#endif
+ }
+}
+
+static void unary_expression(int mode, Value * v);
+
+static void op_sizeof(int mode, Value * v) {
+ Symbol * type = NULL;
+ int pos = 0;
+ int p = text_sy == '(';
+
+ if (p) next_sy();
+ pos = text_pos - 2;
+ if (type_name(mode, &type)) {
+ if (mode != MODE_SKIP) {
+ ContextAddress type_size = 0;
+#if ENABLE_Symbols
+ if (get_symbol_size(type, &type_size) < 0) {
+ error(errno, "Cannot retrieve symbol size");
+ }
+#endif
+ set_ctx_word_value(v, type_size);
+ v->type = NULL;
+ v->type_class = TYPE_CLASS_CARDINAL;
+ v->constant = 1;
+ }
+ }
+ else {
+ text_pos = pos;
+ next_ch();
+ next_sy();
+ unary_expression(mode == MODE_NORMAL ? MODE_TYPE : mode, v);
+ if (mode != MODE_SKIP) {
+ set_ctx_word_value(v, v->size);
+ v->type = NULL;
+ v->type_class = TYPE_CLASS_CARDINAL;
+ v->constant = 1;
+ }
+ }
+ if (p) {
+ if (text_sy != ')') error(ERR_INV_EXPRESSION, "')' expected");
+ next_sy();
+ }
+}
+
+
+static void postfix_expression(int mode, Value * v) {
+ primary_expression(mode, v);
+ for (;;) {
+ if (text_sy == '.') {
+ next_sy();
+ op_field(mode, v);
+ }
+ else if (text_sy == '[') {
+ next_sy();
+ op_index(mode, v);
+ if (text_sy != ']') {
+ error(ERR_INV_EXPRESSION, "']' expected");
+ }
+ next_sy();
+ }
+ else if (text_sy == SY_REF) {
+ next_sy();
+ op_deref(mode, v);
+ op_field(mode, v);
+ }
+ else {
+ break;
+ }
+ }
+}
+
+/* Note: lazy_unary_expression() does not set v->size if v->sym != NULL */
+static void lazy_unary_expression(int mode, Value * v) {
+ switch (text_sy) {
+ case '*':
+ next_sy();
+ lazy_unary_expression(mode, v);
+ op_deref(mode, v);
+ break;
+ case '&':
+ next_sy();
+ lazy_unary_expression(mode, v);
+ op_addr(mode, v);
+ break;
+ case SY_SIZEOF:
+ next_sy();
+ op_sizeof(mode, v);
+ break;
+ case '+':
+ next_sy();
+ lazy_unary_expression(mode, v);
+ break;
+ case '-':
+ next_sy();
+ unary_expression(mode, v);
+ if (mode != MODE_SKIP) {
+ if (!is_number(v)) {
+ error(ERR_INV_EXPRESSION, "Numeric types expected");
+ }
+ else if (v->type_class == TYPE_CLASS_REAL) {
+ set_fp_value(v, sizeof(double), -to_double(mode, v));
+ }
+ else if (v->type_class != TYPE_CLASS_CARDINAL) {
+ int64_t value = -to_int(mode, v);
+ v->type_class = TYPE_CLASS_INTEGER;
+ set_int_value(v, sizeof(int64_t), value);
+ }
+ assert(!v->remote);
+ v->type = NULL;
+ }
+ break;
+ case '!':
+ next_sy();
+ unary_expression(mode, v);
+ if (mode != MODE_SKIP) {
+ if (!is_whole_number(v)) {
+ error(ERR_INV_EXPRESSION, "Integral types expected");
+ }
+ else {
+ int32_t value = !to_int(mode, v);
+ v->type_class = TYPE_CLASS_INTEGER;
+ set_int_value(v, sizeof(int32_t), value);
+ }
+ assert(!v->remote);
+ v->type = NULL;
+ }
+ break;
+ case '~':
+ next_sy();
+ unary_expression(mode, v);
+ if (mode != MODE_SKIP) {
+ if (!is_whole_number(v)) {
+ error(ERR_INV_EXPRESSION, "Integral types expected");
+ }
+ else {
+ int64_t value = ~to_int(mode, v);
+ set_int_value(v, sizeof(int64_t), value);
+ }
+ assert(!v->remote);
+ v->type = NULL;
+ }
+ break;
+#if ENABLE_Symbols
+ case '(':
+ {
+ Symbol * type = NULL;
+ int type_class = TYPE_CLASS_UNKNOWN;
+ ContextAddress type_size = 0;
+ int pos = text_pos - 2;
+
+ assert(text[pos] == '(');
+ next_sy();
+ if (!type_name(mode, &type)) {
+ text_pos = pos;
+ next_ch();
+ next_sy();
+ assert(text_sy == '(');
+ postfix_expression(mode, v);
+ break;
+ }
+ if (text_sy != ')') error(ERR_INV_EXPRESSION, "')' expected");
+ next_sy();
+ unary_expression(mode, v);
+ if (mode == MODE_SKIP) break;
+ if (get_symbol_type_class(type, &type_class) < 0) {
+ error(errno, "Cannot retrieve symbol type class");
+ }
+ if (get_symbol_size(type, &type_size) < 0) {
+ error(errno, "Cannot retrieve symbol size");
+ }
+ if (v->remote && v->size == type_size) {
+ /* A type cast can be an l-value expression as long as the size does not change */
+ int ok = 0;
+ switch (type_class) {
+ case TYPE_CLASS_CARDINAL:
+ case TYPE_CLASS_POINTER:
+ case TYPE_CLASS_INTEGER:
+ case TYPE_CLASS_ENUMERATION:
+ switch (v->type_class) {
+ case TYPE_CLASS_CARDINAL:
+ case TYPE_CLASS_POINTER:
+ case TYPE_CLASS_INTEGER:
+ case TYPE_CLASS_ENUMERATION:
+ ok = 1;
+ break;
+ }
+ break;
+ case TYPE_CLASS_REAL:
+ ok = v->type_class == TYPE_CLASS_REAL;
+ break;
+ }
+ if (ok) {
+ v->type = type;
+ v->type_class = type_class;
+ break;
+ }
+ }
+ switch (type_class) {
+ case TYPE_CLASS_UNKNOWN:
+ error(ERR_INV_EXPRESSION, "Unknown type class");
+ break;
+ case TYPE_CLASS_CARDINAL:
+ case TYPE_CLASS_POINTER:
+ {
+ uint64_t value = to_uns(mode, v);
+ v->type = type;
+ v->type_class = type_class;
+ set_int_value(v, (size_t)type_size, value);
+ }
+ break;
+ case TYPE_CLASS_INTEGER:
+ case TYPE_CLASS_ENUMERATION:
+ {
+ int64_t value = to_int(mode, v);
+ v->type = type;
+ v->type_class = type_class;
+ set_int_value(v, (size_t)type_size, value);
+ }
+ break;
+ case TYPE_CLASS_REAL:
+ {
+ double value = to_double(mode, v);
+ v->type = type;
+ v->type_class = type_class;
+ set_fp_value(v, (size_t)type_size, value);
+ }
+ break;
+ case TYPE_CLASS_ARRAY:
+ if (v->type_class == TYPE_CLASS_POINTER) {
+ v->address = (ContextAddress)to_uns(mode, v);
+ v->sym = NULL;
+ v->reg = NULL;
+ v->type = type;
+ v->type_class = type_class;
+ v->size = type_size;
+ v->big_endian = expression_context->big_endian;
+ v->remote = 1;
+ v->constant = 0;
+ v->value = NULL;
+ }
+ else {
+ error(ERR_INV_EXPRESSION, "Invalid type cast: illegal source type");
+ }
+ break;
+ default:
+ error(ERR_INV_EXPRESSION, "Invalid type cast: illegal destination type");
+ break;
+ }
+ break;
+ }
+#endif
+ default:
+ postfix_expression(mode, v);
+ break;
+ }
+}
+
+static void unary_expression(int mode, Value * v) {
+ lazy_unary_expression(mode, v);
+#if ENABLE_Symbols
+ if (mode != MODE_SKIP && v->sym != NULL && v->size == 0 && get_symbol_size(v->sym, &v->size) < 0) {
+ error(errno, "Cannot retrieve symbol size");
+ }
+#endif
+}
+
+static void multiplicative_expression(int mode, Value * v) {
+ unary_expression(mode, v);
+ while (text_sy == '*' || text_sy == '/' || text_sy == '%') {
+ Value x;
+ int sy = text_sy;
+ next_sy();
+ unary_expression(mode, &x);
+ if (mode != MODE_SKIP) {
+ if (!is_number(v) || !is_number(&x)) {
+ error(ERR_INV_EXPRESSION, "Numeric types expected");
+ }
+ if (mode == MODE_NORMAL && sy != '*' && to_int(mode, &x) == 0) {
+ error(ERR_INV_EXPRESSION, "Dividing by zero");
+ }
+ if (v->type_class == TYPE_CLASS_REAL || x.type_class == TYPE_CLASS_REAL) {
+ double value = 0;
+ if (mode == MODE_NORMAL) {
+ switch (sy) {
+ case '*': value = to_double(mode, v) * to_double(mode, &x); break;
+ case '/': value = to_double(mode, v) / to_double(mode, &x); break;
+ default: error(ERR_INV_EXPRESSION, "Invalid type");
+ }
+ }
+ v->type = NULL;
+ v->type_class = TYPE_CLASS_REAL;
+ set_fp_value(v, sizeof(double), value);
+ }
+ else if (v->type_class == TYPE_CLASS_CARDINAL || x.type_class == TYPE_CLASS_CARDINAL) {
+ uint64_t value = 0;
+ if (mode == MODE_NORMAL) {
+ switch (sy) {
+ case '*': value = to_uns(mode, v) * to_uns(mode, &x); break;
+ case '/': value = to_uns(mode, v) / to_uns(mode, &x); break;
+ case '%': value = to_uns(mode, v) % to_uns(mode, &x); break;
+ }
+ }
+ v->type = NULL;
+ v->type_class = TYPE_CLASS_CARDINAL;
+ set_int_value(v, sizeof(uint64_t), value);
+ }
+ else {
+ int64_t value = 0;
+ if (mode == MODE_NORMAL) {
+ switch (sy) {
+ case '*': value = to_int(mode, v) * to_int(mode, &x); break;
+ case '/': value = to_int(mode, v) / to_int(mode, &x); break;
+ case '%': value = to_int(mode, v) % to_int(mode, &x); break;
+ }
+ }
+ v->type = NULL;
+ v->type_class = TYPE_CLASS_INTEGER;
+ set_int_value(v, sizeof(int64_t), value);
+ }
+ v->constant = v->constant && x.constant;
+ }
+ }
+}
+
+static void additive_expression(int mode, Value * v) {
+ multiplicative_expression(mode, v);
+ while (text_sy == '+' || text_sy == '-') {
+ Value x;
+ int sy = text_sy;
+ next_sy();
+ multiplicative_expression(mode, &x);
+ if (mode != MODE_SKIP) {
+ if (sy == '+' && v->type_class == TYPE_CLASS_ARRAY && x.type_class == TYPE_CLASS_ARRAY) {
+ if (mode == MODE_TYPE) {
+ v->remote = 0;
+ v->size = 0;
+ v->value = alloc_str((size_t)v->size);
+ }
+ else {
+ char * value;
+ load_value(v);
+ load_value(&x);
+ v->size = strlen((char *)v->value) + strlen((char *)x.value) + 1;
+ value = (char *)alloc_str((size_t)v->size);
+ strcpy(value, (const char *)(v->value));
+ strcat(value, (const char *)(x.value));
+ v->value = value;
+ }
+ v->type = NULL;
+ }
+#if ENABLE_Symbols
+ else if ((v->type_class == TYPE_CLASS_POINTER || v->type_class == TYPE_CLASS_ARRAY) && is_number(&x)) {
+ uint64_t value = 0;
+ Symbol * base = NULL;
+ ContextAddress size = 0;
+ if (v->type == NULL || get_symbol_base_type(v->type, &base) < 0 ||
+ base == 0 || get_symbol_size(base, &size) < 0 || size == 0) {
+ error(ERR_INV_EXPRESSION, "Unknown pointer base type size");
+ }
+ switch (sy) {
+ case '+': value = to_uns(mode, v) + to_uns(mode, &x) * size; break;
+ case '-': value = to_uns(mode, v) - to_uns(mode, &x) * size; break;
+ }
+ if (v->type_class == TYPE_CLASS_ARRAY) {
+ if (get_array_symbol(base, 0, &v->type) < 0 ||
+ get_symbol_size(v->type, &v->size) < 0) {
+ error(errno, "Cannot cast to pointer");
+ }
+ v->type_class = TYPE_CLASS_POINTER;
+ }
+ set_int_value(v, (size_t)v->size, value);
+ }
+ else if (is_number(v) && (x.type_class == TYPE_CLASS_POINTER || x.type_class == TYPE_CLASS_ARRAY) && sy == '+') {
+ uint64_t value = 0;
+ Symbol * base = NULL;
+ ContextAddress size = 0;
+ if (x.type == NULL || get_symbol_base_type(x.type, &base) < 0 ||
+ base == 0 || get_symbol_size(base, &size) < 0 || size == 0) {
+ error(ERR_INV_EXPRESSION, "Unknown pointer base type size");
+ }
+ value = to_uns(mode, &x) + to_uns(mode, v) * size;
+ v->type = x.type;
+ if (x.type_class == TYPE_CLASS_ARRAY) {
+ if (get_array_symbol(base, 0, &v->type) < 0 ||
+ get_symbol_size(v->type, &v->size) < 0) {
+ error(errno, "Cannot cast to pointer");
+ }
+ }
+ v->type_class = TYPE_CLASS_POINTER;
+ set_int_value(v, (size_t)x.size, value);
+ }
+#endif
+ else if (!is_number(v) || !is_number(&x)) {
+ error(ERR_INV_EXPRESSION, "Numeric types expected");
+ }
+ else if (v->type_class == TYPE_CLASS_REAL || x.type_class == TYPE_CLASS_REAL) {
+ double value = 0;
+ switch (sy) {
+ case '+': value = to_double(mode, v) + to_double(mode, &x); break;
+ case '-': value = to_double(mode, v) - to_double(mode, &x); break;
+ }
+ v->type = NULL;
+ v->type_class = TYPE_CLASS_REAL;
+ set_fp_value(v, sizeof(double), value);
+ }
+ else if (v->type_class == TYPE_CLASS_CARDINAL || x.type_class == TYPE_CLASS_CARDINAL) {
+ uint64_t value = 0;
+ switch (sy) {
+ case '+': value = to_uns(mode, v) + to_uns(mode, &x); break;
+ case '-': value = to_uns(mode, v) - to_uns(mode, &x); break;
+ }
+ v->type = NULL;
+ v->type_class = TYPE_CLASS_CARDINAL;
+ set_int_value(v, sizeof(uint64_t), value);
+ }
+ else {
+ int64_t value = 0;
+ switch (sy) {
+ case '+': value = to_int(mode, v) + to_int(mode, &x); break;
+ case '-': value = to_int(mode, v) - to_int(mode, &x); break;
+ }
+ v->type = NULL;
+ v->type_class = TYPE_CLASS_INTEGER;
+ set_int_value(v, sizeof(int64_t), value);
+ }
+ v->constant = v->constant && x.constant;
+ }
+ }
+}
+
+static void shift_expression(int mode, Value * v) {
+ additive_expression(mode, v);
+ while (text_sy == SY_SHL || text_sy == SY_SHR) {
+ Value x;
+ int sy = text_sy;
+ next_sy();
+ additive_expression(mode, &x);
+ if (mode != MODE_SKIP) {
+ uint64_t value = 0;
+ if (!is_whole_number(v) || !is_whole_number(&x)) {
+ error(ERR_INV_EXPRESSION, "Integral types expected");
+ }
+ if (x.type_class != TYPE_CLASS_CARDINAL && to_int(mode, &x) < 0) {
+ if (v->type_class == TYPE_CLASS_CARDINAL) {
+ switch (sy) {
+ case SY_SHL: value = to_uns(mode, v) >> -to_int(mode, &x); break;
+ case SY_SHR: value = to_uns(mode, v) << -to_int(mode, &x); break;
+ }
+ }
+ else {
+ switch (sy) {
+ case SY_SHL: value = to_int(mode, v) >> -to_int(mode, &x); break;
+ case SY_SHR: value = to_int(mode, v) << -to_int(mode, &x); break;
+ }
+ v->type_class = TYPE_CLASS_INTEGER;
+ }
+ }
+ else {
+ if (v->type_class == TYPE_CLASS_CARDINAL) {
+ switch (sy) {
+ case SY_SHL: value = to_uns(mode, v) << to_uns(mode, &x); break;
+ case SY_SHR: value = to_uns(mode, v) >> to_uns(mode, &x); break;
+ }
+ }
+ else {
+ switch (sy) {
+ case SY_SHL: value = to_int(mode, v) << to_uns(mode, &x); break;
+ case SY_SHR: value = to_int(mode, v) >> to_uns(mode, &x); break;
+ }
+ v->type_class = TYPE_CLASS_INTEGER;
+ }
+ }
+ v->type = NULL;
+ v->constant = v->constant && x.constant;
+ set_int_value(v, sizeof(uint64_t), value);
+ }
+ }
+}
+
+static void relational_expression(int mode, Value * v) {
+ shift_expression(mode, v);
+ while (text_sy == '<' || text_sy == '>' || text_sy == SY_LEQ || text_sy == SY_GEQ) {
+ Value x;
+ int sy = text_sy;
+ next_sy();
+ shift_expression(mode, &x);
+ if (mode != MODE_SKIP) {
+ uint32_t value = 0;
+ if (v->type_class == TYPE_CLASS_ARRAY && x.type_class == TYPE_CLASS_ARRAY) {
+ int n = 0;
+ load_value(v);
+ load_value(&x);
+ n = strcmp((char *)v->value, (char *)x.value);
+ switch (sy) {
+ case '<': value = n < 0; break;
+ case '>': value = n > 0; break;
+ case SY_LEQ: value = n <= 0; break;
+ case SY_GEQ: value = n >= 0; break;
+ }
+ }
+ else if (v->type_class == TYPE_CLASS_REAL || x.type_class == TYPE_CLASS_REAL) {
+ switch (sy) {
+ case '<': value = to_double(mode, v) < to_double(mode, &x); break;
+ case '>': value = to_double(mode, v) > to_double(mode, &x); break;
+ case SY_LEQ: value = to_double(mode, v) <= to_double(mode, &x); break;
+ case SY_GEQ: value = to_double(mode, v) >= to_double(mode, &x); break;
+ }
+ }
+ else if (v->type_class == TYPE_CLASS_CARDINAL || x.type_class == TYPE_CLASS_CARDINAL) {
+ switch (sy) {
+ case '<': value = to_uns(mode, v) < to_uns(mode, &x); break;
+ case '>': value = to_uns(mode, v) > to_uns(mode, &x); break;
+ case SY_LEQ: value = to_uns(mode, v) <= to_uns(mode, &x); break;
+ case SY_GEQ: value = to_uns(mode, v) >= to_uns(mode, &x); break;
+ }
+ }
+ else {
+ switch (sy) {
+ case '<': value = to_int(mode, v) < to_int(mode, &x); break;
+ case '>': value = to_int(mode, v) > to_int(mode, &x); break;
+ case SY_LEQ: value = to_int(mode, v) <= to_int(mode, &x); break;
+ case SY_GEQ: value = to_int(mode, v) >= to_int(mode, &x); break;
+ }
+ }
+ if (mode != MODE_NORMAL) value = 0;
+ v->type_class = TYPE_CLASS_INTEGER;
+ v->type = NULL;
+ v->constant = v->constant && x.constant;
+ set_int_value(v, sizeof(uint32_t), value);
+ }
+ }
+}
+
+static void equality_expression(int mode, Value * v) {
+ relational_expression(mode, v);
+ while (text_sy == SY_EQU || text_sy == SY_NEQ) {
+ Value x;
+ int sy = text_sy;
+ next_sy();
+ relational_expression(mode, &x);
+ if (mode != MODE_SKIP) {
+ uint32_t value = 0;
+ if (v->type_class == TYPE_CLASS_ARRAY && x.type_class == TYPE_CLASS_ARRAY) {
+ load_value(v);
+ load_value(&x);
+ value = strcmp((char *)v->value, (char *)x.value) == 0;
+ }
+ else if (v->type_class == TYPE_CLASS_REAL || x.type_class == TYPE_CLASS_REAL) {
+ value = to_double(mode, v) == to_double(mode, &x);
+ }
+ else {
+ value = to_int(mode, v) == to_int(mode, &x);
+ }
+ if (sy == SY_NEQ) value = !value;
+ if (mode != MODE_NORMAL) value = 0;
+ v->type_class = TYPE_CLASS_INTEGER;
+ v->type = NULL;
+ v->constant = v->constant && x.constant;
+ set_int_value(v, sizeof(uint32_t), value);
+ }
+ }
+}
+
+static void and_expression(int mode, Value * v) {
+ equality_expression(mode, v);
+ while (text_sy == '&') {
+ Value x;
+ next_sy();
+ equality_expression(mode, &x);
+ if (mode != MODE_SKIP) {
+ int64_t value = 0;
+ if (!is_whole_number(v) || !is_whole_number(&x)) {
+ error(ERR_INV_EXPRESSION, "Integral types expected");
+ }
+ if (v->type_class == TYPE_CLASS_CARDINAL || x.type_class == TYPE_CLASS_CARDINAL) {
+ v->type_class = TYPE_CLASS_CARDINAL;
+ value = to_uns(mode, v) & to_uns(mode, &x);
+ }
+ else {
+ v->type_class = TYPE_CLASS_INTEGER;
+ value = to_int(mode, v) & to_int(mode, &x);
+ }
+ if (mode != MODE_NORMAL) value = 0;
+ v->type = NULL;
+ v->constant = v->constant && x.constant;
+ set_int_value(v, sizeof(int64_t), value);
+ }
+ }
+}
+
+static void exclusive_or_expression(int mode, Value * v) {
+ and_expression(mode, v);
+ while (text_sy == '^') {
+ Value x;
+ next_sy();
+ and_expression(mode, &x);
+ if (mode != MODE_SKIP) {
+ int64_t value = 0;
+ if (!is_whole_number(v) || !is_whole_number(&x)) {
+ error(ERR_INV_EXPRESSION, "Integral types expected");
+ }
+ if (v->type_class == TYPE_CLASS_CARDINAL || x.type_class == TYPE_CLASS_CARDINAL) {
+ v->type_class = TYPE_CLASS_CARDINAL;
+ value = to_uns(mode, v) ^ to_uns(mode, &x);
+ }
+ else {
+ v->type_class = TYPE_CLASS_INTEGER;
+ value = to_int(mode, v) ^ to_int(mode, &x);
+ }
+ if (mode != MODE_NORMAL) value = 0;
+ v->type = NULL;
+ v->constant = v->constant && x.constant;
+ set_int_value(v, sizeof(int64_t), value);
+ }
+ }
+}
+
+static void inclusive_or_expression(int mode, Value * v) {
+ exclusive_or_expression(mode, v);
+ while (text_sy == '|') {
+ Value x;
+ next_sy();
+ exclusive_or_expression(mode, &x);
+ if (mode != MODE_SKIP) {
+ int64_t value = 0;
+ if (!is_whole_number(v) || !is_whole_number(&x)) {
+ error(ERR_INV_EXPRESSION, "Integral types expected");
+ }
+ if (v->type_class == TYPE_CLASS_CARDINAL || x.type_class == TYPE_CLASS_CARDINAL) {
+ v->type_class = TYPE_CLASS_CARDINAL;
+ value = to_uns(mode, v) | to_uns(mode, &x);
+ }
+ else {
+ v->type_class = TYPE_CLASS_INTEGER;
+ value = to_int(mode, v) | to_int(mode, &x);
+ }
+ if (mode != MODE_NORMAL) value = 0;
+ v->type = NULL;
+ v->constant = v->constant && x.constant;
+ set_int_value(v, sizeof(int64_t), value);
+ }
+ }
+}
+
+static void logical_and_expression(int mode, Value * v) {
+ inclusive_or_expression(mode, v);
+ while (text_sy == SY_AND) {
+ Value x;
+ int b = to_boolean(mode, v);
+ next_sy();
+ inclusive_or_expression(b ? mode : MODE_SKIP, &x);
+ if (b) {
+ if (!v->constant) x.constant = 0;
+ *v = x;
+ }
+ }
+}
+
+static void logical_or_expression(int mode, Value * v) {
+ logical_and_expression(mode, v);
+ while (text_sy == SY_OR) {
+ Value x;
+ int b = to_boolean(mode, v);
+ next_sy();
+ logical_and_expression(!b ? mode : MODE_SKIP, &x);
+ if (!b) {
+ if (!v->constant) x.constant = 0;
+ *v = x;
+ }
+ }
+}
+
+static void conditional_expression(int mode, Value * v) {
+ logical_or_expression(mode, v);
+ if (text_sy == '?') {
+ Value x;
+ Value y;
+ int b = to_boolean(mode, v);
+ next_sy();
+ expression(b ? mode : MODE_SKIP, &x);
+ if (text_sy != ':') error(ERR_INV_EXPRESSION, "Missing ':'");
+ next_sy();
+ conditional_expression(!b ? mode : MODE_SKIP, &y);
+ if (!v->constant) x.constant = y.constant = 0;
+ *v = b ? x : y;
+ }
+}
+
+static void expression(int mode, Value * v) {
+ /* TODO: assignments in expressions */
+ conditional_expression(mode, v);
+}
+
+static int evaluate_type(Context * ctx, int frame, ContextAddress addr, char * s, Value * v) {
+ Trap trap;
+
+ expression_context = ctx;
+ expression_frame = frame;
+ expression_addr = addr;
+ if (!set_trap(&trap)) return -1;
+ str_pool_cnt = 0;
+ while (str_alloc_list != NULL) {
+ StringValue * str = str_alloc_list;
+ str_alloc_list = str->next;
+ loc_free(str);
+ }
+ text = s;
+ text_pos = 0;
+ text_len = strlen(s) + 1;
+ next_ch();
+ next_sy();
+ expression(MODE_TYPE, v);
+ if (text_sy != 0) error(ERR_INV_EXPRESSION, "Illegal characters at the end of expression");
+ clear_trap(&trap);
+ return 0;
+}
+
+int evaluate_expression(Context * ctx, int frame, ContextAddress addr, char * s, int load, Value * v) {
+ Trap trap;
+
+ expression_context = ctx;
+ expression_frame = frame;
+ expression_addr = addr;
+ if (!set_trap(&trap)) return -1;
+ if (s == NULL || *s == 0) str_exception(ERR_INV_EXPRESSION, "Empty expression");
+ str_pool_cnt = 0;
+ while (str_alloc_list != NULL) {
+ StringValue * str = str_alloc_list;
+ str_alloc_list = str->next;
+ loc_free(str);
+ }
+ text = s;
+ text_pos = 0;
+ text_len = strlen(s) + 1;
+ next_ch();
+ next_sy();
+ expression(MODE_NORMAL, v);
+ if (text_sy != 0) error(ERR_INV_EXPRESSION, "Illegal characters at the end of expression");
+ if (load) load_value(v);
+ clear_trap(&trap);
+ return 0;
+}
+
+int value_to_boolean(Value * v, int * res) {
+ Trap trap;
+ if (!set_trap(&trap)) return -1;
+ *res = to_boolean(MODE_NORMAL, v);
+ clear_trap(&trap);
+ return 0;
+}
+
+int value_to_address(Value * v, ContextAddress * res) {
+ Trap trap;
+ if (!set_trap(&trap)) return -1;
+ *res = (ContextAddress)to_uns(MODE_NORMAL, v);
+ clear_trap(&trap);
+ return 0;
+}
+
+int value_to_signed(Value * v, int64_t *res) {
+ Trap trap;
+ if (!set_trap(&trap)) return -1;
+ *res = to_int(MODE_NORMAL, v);
+ clear_trap(&trap);
+ return 0;
+}
+
+int value_to_unsigned(Value * v, uint64_t *res) {
+ Trap trap;
+ if (!set_trap(&trap)) return -1;
+ *res = to_uns(MODE_NORMAL, v);
+ clear_trap(&trap);
+ return 0;
+}
+
+int value_to_double(Value * v, double *res) {
+ Trap trap;
+ if (!set_trap(&trap)) return -1;
+ *res = to_double(MODE_NORMAL, v);
+ clear_trap(&trap);
+ return 0;
+}
+
+/********************** Commands **************************/
+
+typedef struct CommandArgs {
+ char token[256];
+ char id[256];
+} CommandArgs;
+
+typedef struct CommandCreateArgs {
+ char token[256];
+ char id[256];
+ char language[256];
+ char * script;
+} CommandCreateArgs;
+
+typedef struct CommandAssignArgs {
+ char token[256];
+ char id[256];
+ char * value_buf;
+ size_t value_size;
+} CommandAssignArgs;
+
+typedef struct Expression {
+ LINK link_all;
+ LINK link_id;
+ char id[256];
+ char var_id[256];
+ char parent[256];
+ char language[256];
+ Channel * channel;
+ char * script;
+ int can_assign;
+ ContextAddress size;
+ int type_class;
+ char type[256];
+} Expression;
+
+#define link_all2exp(A) ((Expression *)((char *)(A) - offsetof(Expression, link_all)))
+#define link_id2exp(A) ((Expression *)((char *)(A) - offsetof(Expression, link_id)))
+
+#define ID2EXP_HASH_SIZE (32 * MEM_USAGE_FACTOR - 1)
+
+static LINK expressions;
+static LINK id2exp[ID2EXP_HASH_SIZE];
+
+#define MAX_SYM_NAME 1024
+
+static const char * EXPRESSIONS = "Expressions";
+static unsigned expr_id_cnt = 0;
+
+#define expression_hash(id) ((unsigned)atoi(id + 4) % ID2EXP_HASH_SIZE)
+
+static Expression * find_expression(char * id) {
+ if (id[0] == 'E' && id[1] == 'X' && id[2] == 'P' && id[3] == 'R') {
+ unsigned hash = expression_hash(id);
+ LINK * l = id2exp[hash].next;
+ while (l != &id2exp[hash]) {
+ Expression * e = link_id2exp(l);
+ l = l->next;
+ if (strcmp(e->id, id) == 0) return e;
+ }
+ }
+ return NULL;
+}
+
+static int symbol_to_expression(char * expr_id, char * parent, char * sym_id, Expression ** res) {
+#if ENABLE_Symbols
+ Symbol * sym = NULL;
+ Symbol * type = NULL;
+ int sym_class = 0;
+ static char script[256];
+ static Expression expr;
+
+ memset(&expr, 0, sizeof(Expression));
+
+ strlcpy(expr.id, expr_id, sizeof(expr.id));
+ strlcpy(expr.var_id, sym_id, sizeof(expr.var_id));
+ strlcpy(expr.parent, parent, sizeof(expr.parent));
+
+ if (id2symbol(sym_id, &sym) < 0) return -1;
+
+ snprintf(script, sizeof(script), "${%s}", sym_id);
+ expr.script = script;
+
+ get_symbol_type_class(sym, &expr.type_class);
+ get_symbol_size(sym, &expr.size);
+
+ if (get_symbol_class(sym, &sym_class) == 0) {
+ expr.can_assign = sym_class == SYM_CLASS_REFERENCE;
+ }
+
+ if (get_symbol_type(sym, &type) == 0 && type != NULL) {
+ strlcpy(expr.type, symbol2id(type), sizeof(expr.type));
+ }
+
+ *res = &expr;
+ return 0;
+#else
+ errno = ERR_UNSUPPORTED;
+ return -1;
+#endif
+}
+
+static int expression_context_id(char * id, Context ** ctx, int * frame, Expression ** expr) {
+ int err = 0;
+ Expression * e = NULL;
+
+ if (id[0] == 'S') {
+ char parent[256];
+ char * s = id + 1;
+ size_t i = 0;
+ while (*s && i < sizeof(parent) - 1) {
+ char ch = *s++;
+ if (ch == '.') {
+ if (*s == '.') {
+ parent[i++] = *s++;
+ continue;
+ }
+ break;
+ }
+ parent[i++] = ch;
+ }
+ parent[i] = 0;
+ if (symbol_to_expression(id, parent, s, &e) < 0) err = errno;
+ }
+ else if ((e = find_expression(id)) == NULL) {
+ err = ERR_INV_CONTEXT;
+ }
+
+ if (!err) {
+ if ((*ctx = id2ctx(e->parent)) != NULL) {
+ *frame = context_has_state(*ctx) ? STACK_TOP_FRAME : STACK_NO_FRAME;
+ }
+ else if (id2frame(e->parent, ctx, frame) < 0) {
+ err = errno;
+ }
+ }
+
+ if (err) {
+ errno = err;
+ return -1;
+ }
+
+ *expr = e;
+ return 0;
+}
+
+static void write_context(OutputStream * out, Expression * expr) {
+ write_stream(out, '{');
+ json_write_string(out, "ID");
+ write_stream(out, ':');
+ json_write_string(out, expr->id);
+
+ write_stream(out, ',');
+
+ json_write_string(out, "ParentID");
+ write_stream(out, ':');
+ json_write_string(out, expr->parent);
+
+ if (expr->var_id[0]) {
+ write_stream(out, ',');
+
+ json_write_string(out, "SymbolID");
+ write_stream(out, ':');
+ json_write_string(out, expr->var_id);
+ }
+
+ write_stream(out, ',');
+
+ json_write_string(out, "Expression");
+ write_stream(out, ':');
+ json_write_string(out, expr->script);
+
+ write_stream(out, ',');
+
+ json_write_string(out, "CanAssign");
+ write_stream(out, ':');
+ json_write_boolean(out, expr->can_assign);
+
+ if (expr->type_class != TYPE_CLASS_UNKNOWN) {
+ write_stream(out, ',');
+
+ json_write_string(out, "Class");
+ write_stream(out, ':');
+ json_write_long(out, expr->type_class);
+ }
+
+ if (expr->type[0]) {
+ write_stream(out, ',');
+
+ json_write_string(out, "Type");
+ write_stream(out, ':');
+ json_write_string(out, expr->type);
+ }
+
+ write_stream(out, ',');
+
+ json_write_string(out, "Size");
+ write_stream(out, ':');
+ json_write_uint64(out, expr->size);
+
+ write_stream(out, '}');
+}
+
+static void get_context_cache_client(void * x) {
+ CommandArgs * args = (CommandArgs *)x;
+ Channel * c = cache_channel();
+ Context * ctx = NULL;
+ int frame = STACK_NO_FRAME;
+ Expression * expr = NULL;
+ int err = 0;
+
+ if (expression_context_id(args->id, &ctx, &frame, &expr) < 0) err = errno;
+
+ cache_exit();
+
+ write_stringz(&c->out, "R");
+ write_stringz(&c->out, args->token);
+ write_errno(&c->out, err);
+
+ if (err) {
+ write_stringz(&c->out, "null");
+ }
+ else {
+ write_context(&c->out, expr);
+ write_stream(&c->out, 0);
+ }
+
+ write_stream(&c->out, MARKER_EOM);
+}
+
+static void command_get_context(char * token, Channel * c) {
+ CommandArgs 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(get_context_cache_client, c, &args, sizeof(args));
+}
+
+#if ENABLE_Symbols
+
+static int sym_cnt = 0;
+static int sym_max = 0;
+static Symbol ** sym_buf = NULL;
+
+static void get_children_callback(void * x, Symbol * symbol) {
+ if (sym_cnt >= sym_max) {
+ sym_max += 8;
+ sym_buf = (Symbol **)loc_realloc(sym_buf, sizeof(Symbol *) * sym_max);
+ }
+ sym_buf[sym_cnt++] = symbol;
+}
+
+#endif
+
+static void get_children_cache_client(void * x) {
+ CommandArgs * args = (CommandArgs *)x;
+ Channel * c = cache_channel();
+ int err = 0;
+
+ /* TODO: Expressions.getChildren - structures */
+#if ENABLE_Symbols
+ char parent_id[256];
+ {
+ Context * ctx;
+ int frame = STACK_NO_FRAME;
+
+ sym_cnt = 0;
+
+ if ((ctx = id2ctx(args->id)) != NULL && context_has_state(ctx)) {
+ frame = get_top_frame(ctx);
+ strlcpy(parent_id, frame2id(ctx, frame), sizeof(parent_id));
+ }
+ else if (id2frame(args->id, &ctx, &frame) == 0) {
+ strlcpy(parent_id, args->id, sizeof(parent_id));
+ }
+ else {
+ ctx = NULL;
+ }
+
+ if (ctx != NULL && err == 0 && enumerate_symbols(
+ ctx, frame, get_children_callback, &args) < 0) err = errno;
+ }
+#else
+ err = ERR_UNSUPPORTED;
+#endif
+
+ cache_exit();
+
+ write_stringz(&c->out, "R");
+ write_stringz(&c->out, args->token);
+
+ write_errno(&c->out, err);
+
+ write_stream(&c->out, '[');
+#if ENABLE_Symbols
+ {
+ int i;
+ for (i = 0; i < sym_cnt; i++) {
+ const char * s = parent_id;
+ if (i > 0) write_stream(&c->out, ',');
+ write_stream(&c->out, '"');
+ write_stream(&c->out, 'S');
+ while (*s) {
+ if (*s == '.') write_stream(&c->out, '.');
+ json_write_char(&c->out, *s++);
+ }
+ write_stream(&c->out, '.');
+ s = symbol2id(sym_buf[i]);
+ while (*s) json_write_char(&c->out, *s++);
+ write_stream(&c->out, '"');
+ }
+ }
+#endif
+ write_stream(&c->out, ']');
+ write_stream(&c->out, 0);
+
+ write_stream(&c->out, MARKER_EOM);
+}
+
+static void command_get_children(char * token, Channel * c) {
+ CommandArgs 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(get_children_cache_client, c, &args, sizeof(args));
+}
+
+static void command_create_cache_client(void * x) {
+ CommandCreateArgs * args = (CommandCreateArgs *)x;
+ Expression * e;
+ Expression buf;
+ Channel * c = cache_channel();
+ int frame = STACK_NO_FRAME;
+ int err = 0;
+
+ memset(e = &buf, 0, sizeof(buf));
+ do snprintf(e->id, sizeof(e->id), "EXPR%d", expr_id_cnt++);
+ while (find_expression(e->id) != NULL);
+ strlcpy(e->parent, args->id, sizeof(e->parent));
+ strlcpy(e->language, args->language, sizeof(e->language));
+ e->channel = c;
+ e->script = args->script;
+
+ if (!err) {
+ Value value;
+ Context * ctx = NULL;
+ memset(&value, 0, sizeof(value));
+ if ((ctx = id2ctx(e->parent)) != NULL) {
+ frame = context_has_state(ctx) ? STACK_TOP_FRAME : STACK_NO_FRAME;
+ }
+ else if (id2frame(e->parent, &ctx, &frame) < 0) {
+ err = errno;
+ }
+ if (!err && evaluate_type(ctx, frame, 0, e->script, &value) < 0) err = errno;
+ if (!err) {
+ e->can_assign = value.remote;
+ e->type_class = value.type_class;
+ e->size = value.size;
+#if ENABLE_Symbols
+ if (value.type != NULL) strlcpy(e->type, symbol2id(value.type), sizeof(e->type));
+#endif
+ }
+ }
+
+ cache_exit();
+
+ write_stringz(&c->out, "R");
+ write_stringz(&c->out, args->token);
+ write_errno(&c->out, err);
+
+ if (err) {
+ write_stringz(&c->out, "null");
+ }
+ else {
+ *(e = (Expression *)loc_alloc(sizeof(Expression))) = buf;
+ list_add_last(&e->link_all, &expressions);
+ list_add_last(&e->link_id, id2exp + expression_hash(e->id));
+ write_context(&c->out, e);
+ write_stream(&c->out, 0);
+ }
+
+ write_stream(&c->out, MARKER_EOM);
+}
+
+static void command_create(char * token, Channel * c) {
+ CommandCreateArgs args;
+
+ json_read_string(&c->inp, args.id, sizeof(args.id));
+ if (read_stream(&c->inp) != 0) exception(ERR_JSON_SYNTAX);
+ json_read_string(&c->inp, args.language, sizeof(args.language));
+ if (read_stream(&c->inp) != 0) exception(ERR_JSON_SYNTAX);
+ args.script = json_read_alloc_string(&c->inp);
+ 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_create_cache_client, c, &args, sizeof(args));
+}
+
+static void command_evaluate_cache_client(void * x) {
+ CommandCreateArgs * args = (CommandCreateArgs *)x;
+ Channel * c = cache_channel();
+ Context * ctx = NULL;
+ int frame = STACK_NO_FRAME;
+ Expression * expr = NULL;
+ int value_ok = 0;
+ Value value;
+ int err = 0;
+
+ memset(&value, 0, sizeof(value));
+ if (expression_context_id(args->id, &ctx, &frame, &expr) < 0) err = errno;
+ if (!err && frame != STACK_NO_FRAME && !ctx->stopped) err = ERR_IS_RUNNING;
+ if (!err && evaluate_expression(ctx, frame, 0, expr->script, 0, &value) < 0) err = errno;
+ if (value.size >= 0x100000) err = ERR_BUFFER_OVERFLOW;
+
+ cache_exit();
+
+ write_stringz(&c->out, "R");
+ write_stringz(&c->out, args->token);
+ if (err) {
+ write_stringz(&c->out, "null");
+ }
+ else {
+ JsonWriteBinaryState state;
+
+ value_ok = 1;
+ json_write_binary_start(&state, &c->out, (size_t)value.size);
+ if (!value.remote) {
+ json_write_binary_data(&state, value.value, (size_t)value.size);
+ }
+ else {
+ char buf[256];
+ size_t offs = 0;
+ while (offs < (size_t)value.size) {
+ int size = (size_t)value.size - offs;
+ if (size > (int)sizeof(buf)) size = (int)sizeof(buf);
+ memset(buf, 0, size);
+ if (!err && context_read_mem(ctx, value.address + offs, buf, size) < 0) err = errno;
+ json_write_binary_data(&state, buf, size);
+ offs += size;
+ }
+ }
+ json_write_binary_end(&state);
+ write_stream(&c->out, 0);
+ }
+ write_errno(&c->out, err);
+ if (!value_ok) {
+ write_stringz(&c->out, "null");
+ }
+ else {
+ int cnt = 0;
+ write_stream(&c->out, '{');
+
+ if (value.type_class != TYPE_CLASS_UNKNOWN) {
+ json_write_string(&c->out, "Class");
+ write_stream(&c->out, ':');
+ json_write_long(&c->out, value.type_class);
+ cnt++;
+ }
+
+#if ENABLE_Symbols
+ if (value.type != NULL) {
+ if (cnt > 0) write_stream(&c->out, ',');
+ json_write_string(&c->out, "Type");
+ write_stream(&c->out, ':');
+ json_write_string(&c->out, symbol2id(value.type));
+ cnt++;
+ }
+#endif
+ if (value.reg != NULL) {
+ if (cnt > 0) write_stream(&c->out, ',');
+ json_write_string(&c->out, "Register");
+ write_stream(&c->out, ':');
+ json_write_string(&c->out, register2id(ctx, frame, value.reg));
+ cnt++;
+ }
+
+ if (value.remote) {
+ if (cnt > 0) write_stream(&c->out, ',');
+ json_write_string(&c->out, "Address");
+ write_stream(&c->out, ':');
+ json_write_uint64(&c->out, value.address);
+ cnt++;
+ }
+
+ if (value.big_endian) {
+ if (cnt > 0) write_stream(&c->out, ',');
+ json_write_string(&c->out, "BigEndian");
+ write_stream(&c->out, ':');
+ json_write_boolean(&c->out, 1);
+ cnt++;
+ }
+
+ write_stream(&c->out, '}');
+ write_stream(&c->out, 0);
+ }
+ write_stream(&c->out, MARKER_EOM);
+}
+
+static void command_evaluate(char * token, Channel * c) {
+ CommandArgs 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_evaluate_cache_client, c, &args, sizeof(args));
+}
+
+static void command_assign_cache_client(void * x) {
+ CommandAssignArgs * args = (CommandAssignArgs *)x;
+ Channel * c = cache_channel();
+ Context * ctx = NULL;
+ int frame = STACK_NO_FRAME;
+ Expression * expr = NULL;
+ Value value;
+ int err = 0;
+
+ memset(&value, 0, sizeof(value));
+ if (expression_context_id(args->id, &ctx, &frame, &expr) < 0) err = errno;
+ if (!err && frame != STACK_NO_FRAME && !ctx->stopped) err = ERR_IS_RUNNING;
+ if (!err && evaluate_expression(ctx, frame, 0, expr->script, 0, &value) < 0) err = errno;
+ if (!err) {
+ if (value.reg != NULL) {
+ StackFrame * info = NULL;
+ if (get_frame_info(ctx, frame, &info) < 0) err = errno;
+ if (!err && write_reg_bytes(info, value.reg, 0, args->value_size, (uint8_t *)args->value_buf) < 0) err = errno;
+#if SERVICE_Registers
+ if (!err) send_event_register_changed(register2id(ctx, frame, value.reg));
+#endif
+ }
+ else if (value.remote) {
+ if (context_write_mem(ctx, value.address, args->value_buf, args->value_size) < 0) err = errno;
+#if SERVICE_Memory
+ if (!err) send_event_memory_changed(ctx, value.address, args->value_size);
+#endif
+ }
+ else {
+ err = ERR_INV_EXPRESSION;
+ }
+ }
+
+ cache_exit();
+
+ write_stringz(&c->out, "R");
+ write_stringz(&c->out, args->token);
+ write_errno(&c->out, err);
+ write_stream(&c->out, MARKER_EOM);
+ loc_free(args->value_buf);
+}
+
+static void command_assign(char * token, Channel * c) {
+ CommandAssignArgs args;
+
+ json_read_string(&c->inp, args.id, sizeof(args.id));
+ if (read_stream(&c->inp) != 0) exception(ERR_JSON_SYNTAX);
+ args.value_buf = json_read_alloc_binary(&c->inp, &args.value_size);
+ 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_assign_cache_client, c, &args, sizeof(args));
+}
+
+static void command_dispose(char * token, Channel * c) {
+ char id[256];
+ int err = 0;
+ Expression * e;
+
+ 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);
+
+ e = find_expression(id);
+ if (e != NULL) {
+ list_remove(&e->link_all);
+ list_remove(&e->link_id);
+ loc_free(e->script);
+ loc_free(e);
+ }
+ else {
+ err = ERR_INV_CONTEXT;
+ }
+
+ write_stringz(&c->out, "R");
+ write_stringz(&c->out, token);
+ write_errno(&c->out, err);
+ write_stream(&c->out, MARKER_EOM);
+}
+
+static void on_channel_close(Channel * c) {
+ LINK * l = expressions.next;
+ while (l != &expressions) {
+ Expression * e = link_all2exp(l);
+ l = l->next;
+ if (e->channel == c) {
+ list_remove(&e->link_all);
+ list_remove(&e->link_id);
+ loc_free(e->script);
+ loc_free(e);
+ }
+ }
+}
+
+void add_identifier_callback(ExpressionIdentifierCallBack * callback) {
+ assert(id_callback_cnt < MAX_ID_CALLBACKS);
+ id_callbacks[id_callback_cnt++] = callback;
+}
+
+void ini_expressions_service(Protocol * proto) {
+ unsigned i;
+ list_init(&expressions);
+ for (i = 0; i < ID2EXP_HASH_SIZE; i++) list_init(id2exp + i);
+ add_channel_close_listener(on_channel_close);
+ add_command_handler(proto, EXPRESSIONS, "getContext", command_get_context);
+ add_command_handler(proto, EXPRESSIONS, "getChildren", command_get_children);
+ add_command_handler(proto, EXPRESSIONS, "create", command_create);
+ add_command_handler(proto, EXPRESSIONS, "evaluate", command_evaluate);
+ add_command_handler(proto, EXPRESSIONS, "assign", command_assign);
+ add_command_handler(proto, EXPRESSIONS, "dispose", command_dispose);
+
+ big_endian = big_endian_host();
+}
+
+#endif /* if SERVICE_Expressions */
diff --git a/agent/tcf/services/expressions.h b/agent/tcf/services/expressions.h
new file mode 100644
index 00000000..41b7ba16
--- /dev/null
+++ b/agent/tcf/services/expressions.h
@@ -0,0 +1,92 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2011 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
+ *******************************************************************************/
+
+/*
+ * Expression evaluation service.
+ */
+
+#ifndef D_expressions
+#define D_expressions
+
+#include <config.h>
+#include <framework/protocol.h>
+#include <framework/context.h>
+#include <services/symbols.h>
+
+#if SERVICE_Expressions
+
+/* Value represents result of expression evaluation */
+struct Value {
+ Symbol * sym; /* Value symbol, can be NULL */
+ Symbol * type; /* Value type symbol, can be NULL */
+ int type_class; /* See symbols.h for type class definitions */
+ void * value; /* Pointer to value data buffer, or NULL if remote value */
+ RegisterDefinition * reg; /* Not NULL if the value represents a register variable */
+ ContextAddress address; /* Address of value data in remote target memory */
+ ContextAddress size; /* Value size in bytes */
+ int remote; /* 1 if value data is in remote target memory, 0 if loaded into a local buffer */
+ int constant; /* 1 if the value is not expected to change during execution of value context */
+ int big_endian; /* 1 if the value is big endian */
+ int function; /* 1 if the value represents a function */
+};
+
+typedef struct Value Value;
+
+/*
+ * ExpressionIdentifierCallBack is called for every identifier found in an expression during evaluation,
+ * If callback knows value of the idenfifier it should fill Value struct and return 1,
+ * otherwise it should return 0.
+ */
+typedef int ExpressionIdentifierCallBack(Context *, int /*frame*/, char * /*name*/, Value *);
+
+/*
+ * Evaluate given expression in given context.
+ * 'ctx' - debug context to use for memory access and symbols lookup.
+ * 'frame' - stack frame to use for registers and local variables values.
+ * 'addr' - instruction address for symbols lookup, ignored if frame != STACK_NO_FRAME.
+ * 's' - the expression text.
+ * If load != 0 then result value is always loaded into a local buffer,
+ * otherwise 'v' can point to a value in the target memory.
+ * Return 0 if no errors, otherwise return -1 and sets errno.
+ */
+extern int evaluate_expression(Context * ctx, int frame, ContextAddress addr, char * s, int load, Value * v);
+
+/*
+ * Cast a Value to another type ("boolean" means 0 or 1).
+ * Returns 0 if no errors, otherwise returns -1 and sets errno.
+ */
+int value_to_boolean(Value *v, int *res);
+int value_to_address(Value *v, ContextAddress *res);
+int value_to_signed(Value *v, int64_t *res);
+int value_to_unsigned(Value *v, uint64_t *res);
+int value_to_double(Value *v, double *res);
+
+/*
+ * Allocate and fill local data buffer for a value.
+ * The buffer is freed automatically at the end of current event dispatch cycle.
+ */
+extern void set_value(Value * v, void * data, size_t size, int big_endian);
+
+/*
+ * Add identifier callback to the list of expression callbacks.
+ * The callbacks are called for each identifier found in an expression during evaluation.
+ */
+extern void add_identifier_callback(ExpressionIdentifierCallBack * callback);
+
+extern void ini_expressions_service(Protocol * proto);
+
+#endif /* SERVICE_Expressions */
+
+#endif /* D_expressions */
diff --git a/agent/tcf/services/filesystem.c b/agent/tcf/services/filesystem.c
new file mode 100644
index 00000000..174a8bbc
--- /dev/null
+++ b/agent/tcf/services/filesystem.c
@@ -0,0 +1,1397 @@
+/*******************************************************************************
+ * 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: file system access (TCF name FileSystem)
+ */
+
+#if defined(__GNUC__) && !defined(_GNU_SOURCE)
+# define _GNU_SOURCE
+#endif
+
+#include <config.h>
+
+#if SERVICE_FileSystem
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#if defined(__CYGWIN__)
+# include <ctype.h>
+#endif
+#if !defined(WIN32) || defined(__CYGWIN__)
+# include <utime.h>
+# include <dirent.h>
+#endif
+#if defined(WIN32)
+# include <Windows.h>
+#endif
+#if defined(_WRS_KERNEL)
+# include <ioLib.h>
+#endif
+#include <framework/myalloc.h>
+#include <framework/asyncreq.h>
+#include <framework/streams.h>
+#include <framework/channel.h>
+#include <framework/link.h>
+#include <framework/trace.h>
+#include <framework/json.h>
+#include <framework/exceptions.h>
+#include <framework/protocol.h>
+#include <services/filesystem.h>
+
+#define BUF_SIZE (128 * MEM_USAGE_FACTOR)
+
+static const char * FILE_SYSTEM = "FileSystem";
+
+static const int
+ TCF_O_READ = 0x00000001,
+ TCF_O_WRITE = 0x00000002,
+ TCF_O_APPEND = 0x00000004,
+ TCF_O_CREAT = 0x00000008,
+ TCF_O_TRUNC = 0x00000010,
+ TCF_O_EXCL = 0x00000020;
+
+static const int
+ ATTR_SIZE = 0x00000001,
+ ATTR_UIDGID = 0x00000002,
+ ATTR_PERMISSIONS = 0x00000004,
+ ATTR_ACMODTIME = 0x00000008;
+
+static const int
+ FSERR_EOF = 0x10001,
+ FSERR_NO_SUCH_FILE = 0x10002,
+ FSERR_PERMISSION_DENIED = 0x10003;
+
+#define REQ_READ 1
+#define REQ_WRITE 2
+#define REQ_FSTAT 3
+#define REQ_FSETSTAT 4
+#define REQ_CLOSE 5
+
+typedef struct OpenFileInfo OpenFileInfo;
+typedef struct IORequest IORequest;
+typedef struct FileAttrs FileAttrs;
+
+struct FileAttrs {
+ int flags;
+ int64_t size;
+ int uid;
+ int gid;
+ int permissions;
+ uint64_t atime;
+ uint64_t mtime;
+#if defined(WIN32)
+ DWORD win32_attrs;
+#endif
+};
+
+struct OpenFileInfo {
+ unsigned long handle;
+ char path[FILE_PATH_SIZE];
+ int file;
+ DIR * dir;
+ InputStream * inp;
+ OutputStream * out;
+ LINK link_ring;
+ LINK link_hash;
+ LINK link_reqs;
+ IORequest * posted_req;
+};
+
+struct IORequest {
+ int req;
+ char token[256];
+ OpenFileInfo * handle;
+ FileAttrs attrs;
+ AsyncReqInfo info;
+ LINK link_reqs;
+};
+
+#define hash2file(A) ((OpenFileInfo *)((char *)(A) - offsetof(OpenFileInfo, link_hash)))
+#define ring2file(A) ((OpenFileInfo *)((char *)(A) - offsetof(OpenFileInfo, link_ring)))
+#define reqs2req(A) ((IORequest *)((char *)(A) - offsetof(IORequest, link_reqs)))
+
+static unsigned long handle_cnt = 0;
+
+#define HANDLE_HASH_SIZE (4 * MEM_USAGE_FACTOR - 1)
+static LINK handle_hash[HANDLE_HASH_SIZE];
+static LINK file_info_ring = { NULL, NULL };
+
+static OpenFileInfo * create_open_file_info(Channel * ch, char * path, int file, DIR * dir) {
+ LINK * list_head = NULL;
+
+ OpenFileInfo * h = (OpenFileInfo *)loc_alloc_zero(sizeof(OpenFileInfo));
+ for (;;) {
+ LINK * list_next;
+ OpenFileInfo * p = NULL;
+ h->handle = handle_cnt++;
+ list_head = &handle_hash[h->handle % HANDLE_HASH_SIZE];
+ for (list_next = list_head->next; list_next != list_head; list_next = list_next->next) {
+ if (hash2file(list_next)->handle == h->handle) {
+ p = hash2file(list_next);
+ break;
+ }
+ }
+ if (p == NULL) break;
+ }
+ strcpy(h->path, path);
+ h->file = file;
+ h->dir = dir;
+ h->inp = &ch->inp;
+ h->out = &ch->out;
+ list_add_first(&h->link_ring, &file_info_ring);
+ list_add_first(&h->link_hash, list_head);
+ list_init(&h->link_reqs);
+ return h;
+}
+
+static OpenFileInfo * find_open_file_info(char * id) {
+ unsigned long handle = 0;
+ LINK * list_head = NULL;
+ LINK * list_next = NULL;
+
+ if (id == NULL || id[0] != 'F' || id[1] != 'S' || id[2] == 0) return NULL;
+ handle = strtoul(id + 2, &id, 10);
+ if (id[0] != 0) return NULL;
+ list_head = &handle_hash[handle % HANDLE_HASH_SIZE];
+ for (list_next = list_head->next; list_next != list_head; list_next = list_next->next) {
+ if (hash2file(list_next)->handle == handle) return hash2file(list_next);
+ }
+ return NULL;
+}
+
+static void delete_open_file_info(OpenFileInfo * h) {
+ assert(list_is_empty(&h->link_reqs));
+ list_remove(&h->link_ring);
+ list_remove(&h->link_hash);
+ loc_free(h);
+}
+
+static void channel_close_listener(Channel * c) {
+ LINK list;
+ LINK * list_next = NULL;
+
+ list_init(&list);
+ for (list_next = file_info_ring.next; list_next != &file_info_ring; list_next = list_next->next) {
+ OpenFileInfo * h = ring2file(list_next);
+ if (h->inp == &c->inp) {
+ trace(LOG_ALWAYS, "file handle left open by client: FS%d", h->handle);
+ list_remove(&h->link_hash);
+ if (h->dir != NULL) {
+ closedir(h->dir);
+ h->dir = NULL;
+ }
+ if (h->file >= 0) {
+ int posted = 0;
+ while (!list_is_empty(&h->link_reqs)) {
+ LINK * link = h->link_reqs.next;
+ IORequest * req = reqs2req(link);
+ list_remove(link);
+ if (h->posted_req == req) {
+ req->handle = NULL;
+ posted = 1;
+ }
+ else {
+ loc_free(req->info.u.fio.bufp);
+ loc_free(req);
+ }
+ }
+ if (!posted) close(h->file);
+ }
+ list_add_last(&h->link_hash, &list);
+ }
+ }
+
+ while (!list_is_empty(&list)) delete_open_file_info(hash2file(list.next));
+}
+
+static void write_fs_errno(OutputStream * out, int err) {
+ switch (err) {
+ case ERR_EOF:
+ write_service_error(out, err, FILE_SYSTEM, FSERR_EOF);
+ break;
+ case ENOENT:
+ write_service_error(out, err, FILE_SYSTEM, FSERR_NO_SUCH_FILE);
+ break;
+ case EACCES:
+ write_service_error(out, err, FILE_SYSTEM, FSERR_PERMISSION_DENIED);
+ break;
+ default:
+ write_errno(out, err);
+ break;
+ }
+}
+
+static void write_file_handle(OutputStream * out, OpenFileInfo * h) {
+ if (h == NULL) {
+ write_string(out, "null");
+ }
+ else {
+ char s[32];
+ char * p = s + sizeof(s);
+ unsigned long n = h->handle;
+ *(--p) = 0;
+ do {
+ *(--p) = (char)(n % 10 + '0');
+ n = n / 10;
+ }
+ while (n != 0);
+ *(--p) = 'S';
+ *(--p) = 'F';
+ json_write_string(out, p);
+ }
+ write_stream(out, 0);
+}
+
+static void fill_attrs(FileAttrs * attrs, struct stat * buf) {
+ memset(attrs, 0, sizeof(FileAttrs));
+ attrs->flags |= ATTR_SIZE | ATTR_UIDGID | ATTR_PERMISSIONS | ATTR_ACMODTIME;
+ attrs->size = buf->st_size;
+ attrs->uid = buf->st_uid;
+ attrs->gid = buf->st_gid;
+ attrs->permissions = buf->st_mode;
+ attrs->atime = (uint64_t)buf->st_atime * 1000;
+ attrs->mtime = (uint64_t)buf->st_mtime * 1000;
+}
+
+static void read_file_attrs(InputStream * inp, const char * nm, void * arg) {
+ FileAttrs * attrs = (FileAttrs *)arg;
+ if (strcmp(nm, "Size") == 0) {
+ attrs->size = json_read_int64(inp);
+ attrs->flags |= ATTR_SIZE;
+ }
+ else if (strcmp(nm, "UID") == 0) {
+ attrs->uid = (int)json_read_long(inp);
+ attrs->flags |= ATTR_UIDGID;
+ }
+ else if (strcmp(nm, "GID") == 0) {
+ attrs->gid = (int)json_read_long(inp);
+ attrs->flags |= ATTR_UIDGID;
+ }
+ else if (strcmp(nm, "Permissions") == 0) {
+ attrs->permissions = (int)json_read_long(inp);
+ attrs->flags |= ATTR_PERMISSIONS;
+ }
+ else if (strcmp(nm, "ATime") == 0) {
+ attrs->atime = json_read_uint64(inp);
+ attrs->flags |= ATTR_ACMODTIME;
+ }
+ else if (strcmp(nm, "MTime") == 0) {
+ attrs->mtime = json_read_uint64(inp);
+ attrs->flags |= ATTR_ACMODTIME;
+ }
+#if defined(WIN32)
+ else if (strcmp(nm, "Win32Attrs") == 0) {
+ attrs->win32_attrs = json_read_ulong(inp);
+ }
+#endif
+ else {
+ exception(ERR_JSON_SYNTAX);
+ }
+}
+
+static void write_file_attrs(OutputStream * out, FileAttrs * attrs) {
+ int cnt = 0;
+
+ if (attrs == NULL) {
+ write_stringz(out, "null");
+ return;
+ }
+
+ write_stream(out, '{');
+ if (attrs->flags & ATTR_SIZE) {
+ json_write_string(out, "Size");
+ write_stream(out, ':');
+ json_write_int64(out, attrs->size);
+ cnt++;
+ }
+ if (attrs->flags & ATTR_UIDGID) {
+ if (cnt) write_stream(out, ',');
+ json_write_string(out, "UID");
+ write_stream(out, ':');
+ json_write_long(out, attrs->uid);
+ write_stream(out, ',');
+ json_write_string(out, "GID");
+ write_stream(out, ':');
+ json_write_long(out, attrs->gid);
+ cnt++;
+ }
+ if (attrs->flags & ATTR_PERMISSIONS) {
+ if (cnt) write_stream(out, ',');
+ json_write_string(out, "Permissions");
+ write_stream(out, ':');
+ json_write_long(out, attrs->permissions);
+ cnt++;
+ }
+ if (attrs->flags & ATTR_ACMODTIME) {
+ if (cnt) write_stream(out, ',');
+ json_write_string(out, "ATime");
+ write_stream(out, ':');
+ json_write_uint64(out, attrs->atime);
+ write_stream(out, ',');
+ json_write_string(out, "MTime");
+ write_stream(out, ':');
+ json_write_uint64(out, attrs->mtime);
+ cnt++;
+ }
+
+#if defined(WIN32)
+ if (attrs->win32_attrs != INVALID_FILE_ATTRIBUTES) {
+ if (cnt) write_stream(out, ',');
+ json_write_string(out, "Win32Attrs");
+ write_stream(out, ':');
+ json_write_ulong(out, attrs->win32_attrs);
+ cnt++;
+ }
+#endif
+
+ write_stream(out, '}');
+}
+
+static int to_local_open_flags(int flags) {
+ int res = O_BINARY | O_LARGEFILE;
+ if (flags & TCF_O_READ) res |= O_RDONLY;
+ if (flags & TCF_O_WRITE) res |= O_WRONLY;
+ if (flags & TCF_O_APPEND) res |= O_APPEND;
+ if (flags & TCF_O_CREAT) res |= O_CREAT;
+ if (flags & TCF_O_TRUNC) res |= O_TRUNC;
+ if (flags & TCF_O_EXCL) res |= O_EXCL;
+ return res;
+}
+
+static void read_path(InputStream * inp, char * path, int size) {
+ int i = 0;
+ char buf[FILE_PATH_SIZE];
+ json_read_string(inp, path, size);
+ if (path[0] == 0) strlcpy(path, get_user_home(), size);
+ for (i = 0; path[i] != 0; i++) {
+ if (path[i] == '\\') path[i] = '/';
+ }
+#if defined(__CYGWIN__)
+ if (path[0] != '/' && !(path[0] != 0 && path[1] == ':' && path[2] == '/')) {
+ snprintf(buf, sizeof(buf), "%s/%s", get_user_home(), path);
+ strlcpy(path, buf, size);
+ for (i = 0; path[i] != 0; i++) {
+ if (path[i] == '\\') path[i] = '/';
+ }
+ }
+ if (path[0] != 0 && path[1] == ':' && path[2] == '/') {
+ if (path[3] == 0) {
+ snprintf(buf, sizeof(buf), "/cygdrive/%c", tolower((int)path[0]));
+ }
+ else {
+ snprintf(buf, sizeof(buf), "/cygdrive/%c/%s", tolower((int)path[0]), path + 3);
+ }
+ strlcpy(path, buf, size);
+ }
+#elif defined(WIN32)
+ if (path[0] != 0 && path[1] == ':' && path[2] == '/') return;
+#elif defined(_WRS_KERNEL)
+ {
+ extern DL_LIST iosDvList;
+ DEV_HDR * dev;
+ for (dev = (DEV_HDR *)DLL_FIRST(&iosDvList); dev != NULL; dev = (DEV_HDR *)DLL_NEXT(&dev->node)) {
+ if (strncmp(path, dev->name, strlen(dev->name)) == 0) return;
+ }
+ }
+#endif
+ if (path[0] != '/') {
+ snprintf(buf, sizeof(buf), "%s/%s", get_user_home(), path);
+ strlcpy(path, buf, size);
+ for (i = 0; path[i] != 0; i++) {
+ if (path[i] == '\\') path[i] = '/';
+ }
+ }
+ assert(path[0] == '/');
+}
+
+static void command_open(char * token, Channel * c) {
+ char path[FILE_PATH_SIZE];
+ unsigned long flags = 0;
+ FileAttrs attrs;
+ int file = -1;
+ int err = 0;
+ OpenFileInfo * handle = NULL;
+
+ read_path(&c->inp, path, sizeof(path));
+ if (read_stream(&c->inp) != 0) exception(ERR_JSON_SYNTAX);
+ flags = json_read_ulong(&c->inp);
+ if (read_stream(&c->inp) != 0) exception(ERR_JSON_SYNTAX);
+ memset(&attrs, 0, sizeof(FileAttrs));
+#if defined(WIN32)
+ attrs.win32_attrs = INVALID_FILE_ATTRIBUTES;
+#endif
+ json_read_struct(&c->inp, read_file_attrs, &attrs);
+ if (read_stream(&c->inp) != 0) exception(ERR_JSON_SYNTAX);
+ if (read_stream(&c->inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
+
+ if ((attrs.flags & ATTR_PERMISSIONS) == 0) {
+ attrs.permissions = 0775;
+ }
+ file = open(path, to_local_open_flags(flags), attrs.permissions);
+
+ if (file < 0) {
+ err = errno;
+ }
+ else {
+#if defined(WIN32)
+ if (attrs.win32_attrs != INVALID_FILE_ATTRIBUTES) {
+ if (SetFileAttributes(path, attrs.win32_attrs) == 0)
+ err = set_win32_errno(GetLastError());
+ }
+#endif
+ handle = create_open_file_info(c, path, file, NULL);
+ }
+
+ write_stringz(&c->out, "R");
+ write_stringz(&c->out, token);
+ write_fs_errno(&c->out, err);
+ write_file_handle(&c->out, handle);
+ write_stream(&c->out, MARKER_EOM);
+}
+
+static void reply_close(char * token, OutputStream * out, int err) {
+ write_stringz(out, "R");
+ write_stringz(out, token);
+ write_fs_errno(out, err);
+ write_stream(out, MARKER_EOM);
+}
+
+static void reply_read(char * token, OutputStream * out, int err, void * buf, unsigned len, int eof) {
+ write_stringz(out, "R");
+ write_stringz(out, token);
+ json_write_binary(out, buf, len);
+ write_stream(out, 0);
+ write_fs_errno(out, err);
+ json_write_boolean(out, eof);
+ write_stream(out, 0);
+ write_stream(out, MARKER_EOM);
+}
+
+static void reply_write(char * token, OutputStream * out, int err) {
+ write_stringz(out, "R");
+ write_stringz(out, token);
+ write_fs_errno(out, err);
+ write_stream(out, MARKER_EOM);
+}
+
+static void reply_stat(char * token, OutputStream * out, int err, struct stat * buf, const char * path) {
+ FileAttrs attrs;
+
+ if (err == 0) fill_attrs(&attrs, buf);
+ else memset(&attrs, 0, sizeof(attrs));
+
+#if defined(WIN32)
+ attrs.win32_attrs = err ? INVALID_FILE_ATTRIBUTES : GetFileAttributes(path);
+#endif
+
+ write_stringz(out, "R");
+ write_stringz(out, token);
+ write_fs_errno(out, err);
+ write_file_attrs(out, &attrs);
+ write_stream(out, 0);
+ write_stream(out, MARKER_EOM);
+}
+
+static void reply_setstat(char * token, OutputStream * out, int err) {
+ write_stringz(out, "R");
+ write_stringz(out, token);
+ write_fs_errno(out, err);
+ write_stream(out, MARKER_EOM);
+}
+
+static void post_io_request(OpenFileInfo * handle);
+
+static void done_io_request(void * arg) {
+ int err = 0;
+ IORequest * req = (IORequest *)((AsyncReqInfo *)arg)->client_data;
+ OpenFileInfo * handle = req->handle;
+
+ if (handle == NULL) {
+ /* Abandoned I/O request, channel is already closed */
+ switch (req->req) {
+ case REQ_READ:
+ case REQ_WRITE:
+ close(req->info.u.fio.fd);
+ break;
+ case REQ_CLOSE:
+ break;
+ default:
+ assert(0);
+ }
+ loc_free(req->info.u.fio.bufp);
+ loc_free(req);
+ return;
+ }
+
+ assert(handle->posted_req == req);
+ assert(&handle->posted_req->link_reqs == handle->link_reqs.next);
+ handle->posted_req = NULL;
+ list_remove(&req->link_reqs);
+
+ switch (req->req) {
+ case REQ_READ:
+ if (req->info.error) {
+ reply_read(req->token, handle->out, req->info.error, NULL, 0, 0);
+ }
+ else {
+ reply_read(req->token, handle->out, 0,
+ req->info.u.fio.bufp, req->info.u.fio.rval,
+ (size_t)req->info.u.fio.rval < req->info.u.fio.bufsz);
+ }
+ break;
+ case REQ_WRITE:
+ if (req->info.error) err = req->info.error;
+ else if ((size_t)req->info.u.fio.rval < req->info.u.fio.bufsz) err = ENOSPC;
+ reply_write(req->token, handle->out, err);
+ break;
+ case REQ_CLOSE:
+ err = req->info.error;
+ reply_close(req->token, handle->out, err);
+ if (err == 0) {
+ loc_free(req);
+ while (!list_is_empty(&handle->link_reqs)) {
+ LINK * link = handle->link_reqs.next;
+ req = reqs2req(link);
+ switch (req->req) {
+ case REQ_READ:
+ reply_read(req->token, handle->out, EBADF, NULL, 0, 0);
+ break;
+ case REQ_WRITE:
+ reply_write(req->token, handle->out, EBADF);
+ break;
+ case REQ_FSTAT:
+ reply_stat(req->token, handle->out, EBADF, NULL, NULL);
+ break;
+ case REQ_FSETSTAT:
+ reply_setstat(req->token, handle->out, EBADF);
+ break;
+ case REQ_CLOSE:
+ reply_close(req->token, handle->out, EBADF);
+ break;
+ default:
+ assert(0);
+ }
+ list_remove(link);
+ loc_free(req->info.u.fio.bufp);
+ loc_free(req);
+ }
+ delete_open_file_info(handle);
+ return;
+ }
+ break;
+ default:
+ assert(0);
+ }
+
+ loc_free(req->info.u.fio.bufp);
+ loc_free(req);
+ post_io_request(handle);
+}
+
+static void post_io_request(OpenFileInfo * handle) {
+ while (handle->posted_req == NULL && !list_is_empty(&handle->link_reqs)) {
+ LINK * link = handle->link_reqs.next;
+ IORequest * req = reqs2req(link);
+ switch (req->req) {
+ case REQ_FSTAT:
+ {
+ int err = 0;
+ struct stat buf;
+ memset(&buf, 0, sizeof(buf));
+ if (fstat(handle->file, &buf) < 0) err = errno;
+ reply_stat(req->token, handle->out, err, &buf, handle->path);
+ list_remove(link);
+ loc_free(req);
+ }
+ continue;
+ case REQ_FSETSTAT:
+ {
+ int err = 0;
+ FileAttrs attrs = req->attrs;
+ if (attrs.flags & ATTR_SIZE) {
+ if (ftruncate(handle->file, attrs.size) < 0) err = errno;
+ }
+#if defined(WIN32) || defined(_WRS_KERNEL)
+ if (attrs.flags & ATTR_PERMISSIONS) {
+ if (chmod(handle->path, attrs.permissions) < 0) err = errno;
+ }
+#if defined(WIN32)
+ if (attrs.win32_attrs != INVALID_FILE_ATTRIBUTES) {
+ if (SetFileAttributes(handle->path, attrs.win32_attrs) == 0)
+ err = set_win32_errno(GetLastError());
+ }
+#endif
+#else
+ if (attrs.flags & ATTR_UIDGID) {
+ if (fchown(handle->file, attrs.uid, attrs.gid) < 0) err = errno;
+ }
+ if (attrs.flags & ATTR_PERMISSIONS) {
+ if (fchmod(handle->file, attrs.permissions) < 0) err = errno;
+ }
+#endif
+ if (attrs.flags & ATTR_ACMODTIME) {
+ struct utimbuf buf;
+ buf.actime = (time_t)(attrs.atime / 1000);
+ buf.modtime = (time_t)(attrs.mtime / 1000);
+ if (utime(handle->path, &buf) < 0) err = errno;
+ }
+ reply_setstat(req->token, handle->out, err);
+ list_remove(link);
+ loc_free(req);
+ }
+ continue;
+ }
+ handle->posted_req = req;
+ async_req_post(&req->info);
+ }
+}
+
+static IORequest * create_io_request(char * token, OpenFileInfo * handle, int type) {
+ IORequest * req = (IORequest *)loc_alloc_zero(sizeof(IORequest));
+ req->req = type;
+ req->handle = handle;
+ req->info.done = done_io_request;
+ req->info.client_data = req;
+ strlcpy(req->token, token, sizeof(req->token));
+ list_add_last(&req->link_reqs, &handle->link_reqs);
+ return req;
+}
+
+static void command_close(char * token, Channel * c) {
+ char id[256];
+ OpenFileInfo * h = NULL;
+ int err = 0;
+
+ 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);
+
+ h = find_open_file_info(id);
+ if (h == NULL) {
+ err = EBADF;
+ }
+ else if (h->dir != NULL) {
+ if (closedir(h->dir) < 0) {
+ err = errno;
+ }
+ else {
+ delete_open_file_info(h);
+ }
+ }
+ else {
+ IORequest * req = create_io_request(token, h, REQ_CLOSE);
+ req->info.type = AsyncReqClose;
+ req->info.u.fio.fd = h->file;
+ post_io_request(h);
+ return;
+ }
+
+ reply_close(token, &c->out, err);
+}
+
+static void command_read(char * token, Channel * c) {
+ char id[256];
+ OpenFileInfo * h = NULL;
+ int64_t offset;
+ unsigned long len;
+
+ json_read_string(&c->inp, id, sizeof(id));
+ if (read_stream(&c->inp) != 0) exception(ERR_JSON_SYNTAX);
+ offset = json_read_int64(&c->inp);
+ if (read_stream(&c->inp) != 0) exception(ERR_JSON_SYNTAX);
+ len = json_read_ulong(&c->inp);
+ if (read_stream(&c->inp) != 0) exception(ERR_JSON_SYNTAX);
+ if (read_stream(&c->inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
+
+ h = find_open_file_info(id);
+ if (h == NULL) {
+ reply_read(token, &c->out, EBADF, NULL, 0, 0);
+ }
+ else {
+ IORequest * req = create_io_request(token, h, REQ_READ);
+ if (offset < 0) {
+ req->info.type = AsyncReqRead;
+ }
+ else {
+ req->info.type = AsyncReqSeekRead;
+ req->info.u.fio.offset = offset;
+ }
+ req->info.u.fio.fd = h->file;
+ req->info.u.fio.bufp = loc_alloc(len);
+ req->info.u.fio.bufsz = len;
+ post_io_request(h);
+ }
+}
+
+static void command_write(char * token, Channel * c) {
+ char id[256];
+ OpenFileInfo * h = NULL;
+ int64_t offset;
+ size_t len = 0;
+ JsonReadBinaryState state;
+
+ static size_t buf_size = 0;
+ static char * buf = NULL;
+
+ json_read_string(&c->inp, id, sizeof(id));
+ if (read_stream(&c->inp) != 0) exception(ERR_JSON_SYNTAX);
+ offset = json_read_int64(&c->inp);
+ if (read_stream(&c->inp) != 0) exception(ERR_JSON_SYNTAX);
+
+ json_read_binary_start(&state, &c->inp);
+
+ h = find_open_file_info(id);
+ for (;;) {
+ size_t rd;
+ if (buf_size < len + BUF_SIZE) {
+ buf_size += BUF_SIZE;
+ buf = (char *)loc_realloc(buf, buf_size);
+ }
+ rd = json_read_binary_data(&state, buf + len, buf_size - len);
+ if (rd == 0) break;
+ len += rd;
+ }
+ json_read_binary_end(&state);
+ if (read_stream(&c->inp) != 0) exception(ERR_JSON_SYNTAX);
+ if (read_stream(&c->inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
+
+ if (h == NULL) {
+ reply_write(token, &c->out, EBADF);
+ }
+ else {
+ IORequest * req = create_io_request(token, h, REQ_WRITE);
+ if (offset < 0) {
+ req->info.type = AsyncReqWrite;
+ }
+ else {
+ req->info.type = AsyncReqSeekWrite;
+ req->info.u.fio.offset = offset;
+ }
+ req->info.u.fio.fd = h->file;
+ req->info.u.fio.bufp = loc_alloc(len);
+ req->info.u.fio.bufsz = len;
+ memcpy(req->info.u.fio.bufp, buf, len);
+ post_io_request(h);
+ }
+}
+
+static void command_stat(char * token, Channel * c) {
+ char path[FILE_PATH_SIZE];
+ struct stat buf;
+ int err = 0;
+
+ read_path(&c->inp, path, sizeof(path));
+ if (read_stream(&c->inp) != 0) exception(ERR_JSON_SYNTAX);
+ if (read_stream(&c->inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
+
+ memset(&buf, 0, sizeof(buf));
+ if (stat(path, &buf) < 0) err = errno;
+
+ reply_stat(token, &c->out, err, &buf, path);
+}
+
+static void command_lstat(char * token, Channel * c) {
+ char path[FILE_PATH_SIZE];
+ struct stat buf;
+ int err = 0;
+
+ read_path(&c->inp, path, sizeof(path));
+ if (read_stream(&c->inp) != 0) exception(ERR_JSON_SYNTAX);
+ if (read_stream(&c->inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
+
+ memset(&buf, 0, sizeof(buf));
+ if (lstat(path, &buf) < 0) err = errno;
+
+ reply_stat(token, &c->out, err, &buf, path);
+}
+
+static void command_fstat(char * token, Channel * c) {
+ char id[256];
+ OpenFileInfo * h = 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);
+
+ h = find_open_file_info(id);
+ if (h == NULL) {
+ reply_stat(token, &c->out, EBADF, NULL, NULL);
+ }
+ else {
+ create_io_request(token, h, REQ_FSTAT);
+ post_io_request(h);
+ }
+}
+
+static void command_setstat(char * token, Channel * c) {
+ char path[FILE_PATH_SIZE];
+ FileAttrs attrs;
+ int err = 0;
+
+ read_path(&c->inp, path, sizeof(path));
+ if (read_stream(&c->inp) != 0) exception(ERR_JSON_SYNTAX);
+ memset(&attrs, 0, sizeof(FileAttrs));
+#if defined(WIN32)
+ attrs.win32_attrs = INVALID_FILE_ATTRIBUTES;
+#endif
+ json_read_struct(&c->inp, read_file_attrs, &attrs);
+ if (read_stream(&c->inp) != 0) exception(ERR_JSON_SYNTAX);
+ if (read_stream(&c->inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
+
+ if (attrs.flags & ATTR_SIZE) {
+ if (truncate(path, attrs.size) < 0) err = errno;
+ }
+#if !defined(WIN32) && !defined(_WRS_KERNEL)
+ if (attrs.flags & ATTR_UIDGID) {
+ if (chown(path, attrs.uid, attrs.gid) < 0) err = errno;
+ }
+#endif
+ if (attrs.flags & ATTR_PERMISSIONS) {
+ if (chmod(path, attrs.permissions) < 0) err = errno;
+ }
+ if (attrs.flags & ATTR_ACMODTIME) {
+ struct utimbuf buf;
+ buf.actime = (time_t)(attrs.atime / 1000);
+ buf.modtime = (time_t)(attrs.mtime / 1000);
+ if (utime(path, &buf) < 0) err = errno;
+ }
+#if defined(WIN32)
+ if (attrs.win32_attrs != INVALID_FILE_ATTRIBUTES) {
+ if (SetFileAttributes(path, attrs.win32_attrs) == 0)
+ err = set_win32_errno(GetLastError());
+ }
+#endif
+
+ reply_setstat(token, &c->out, err);
+}
+
+static void command_fsetstat(char * token, Channel * c) {
+ char id[256];
+ FileAttrs attrs;
+ OpenFileInfo * h = NULL;
+
+ json_read_string(&c->inp, id, sizeof(id));
+ if (read_stream(&c->inp) != 0) exception(ERR_JSON_SYNTAX);
+ memset(&attrs, 0, sizeof(FileAttrs));
+#if defined(WIN32)
+ attrs.win32_attrs = INVALID_FILE_ATTRIBUTES;
+#endif
+ json_read_struct(&c->inp, read_file_attrs, &attrs);
+ if (read_stream(&c->inp) != 0) exception(ERR_JSON_SYNTAX);
+ if (read_stream(&c->inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
+
+ h = find_open_file_info(id);
+ if (h == NULL) {
+ reply_setstat(token, &c->out, EBADF);
+ }
+ else {
+ IORequest * req = create_io_request(token, h, REQ_FSETSTAT);
+ req->attrs = attrs;
+ post_io_request(h);
+ }
+}
+
+static void command_opendir(char * token, Channel * c) {
+ char path[FILE_PATH_SIZE];
+ DIR * dir = NULL;
+ int err = 0;
+ OpenFileInfo * handle = NULL;
+
+ read_path(&c->inp, path, sizeof(path));
+ if (read_stream(&c->inp) != 0) exception(ERR_JSON_SYNTAX);
+ if (read_stream(&c->inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
+
+ dir = opendir(path);
+ if (dir == NULL) {
+ err = errno;
+ }
+ else {
+ handle = create_open_file_info(c, path, -1, dir);
+ }
+
+ write_stringz(&c->out, "R");
+ write_stringz(&c->out, token);
+ write_fs_errno(&c->out, err);
+ write_file_handle(&c->out, handle);
+ write_stream(&c->out, MARKER_EOM);
+}
+
+static void command_readdir(char * token, Channel * c) {
+ char id[256];
+ OpenFileInfo * h = NULL;
+ int err = 0;
+ int eof = 0;
+
+ 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);
+
+ write_stringz(&c->out, "R");
+ write_stringz(&c->out, token);
+
+ h = find_open_file_info(id);
+ if (h == NULL || h->dir == NULL) {
+ write_stringz(&c->out, "null");
+ err = EBADF;
+ }
+ else {
+ int cnt = 0;
+ write_stream(&c->out, '[');
+ while (cnt < 64) {
+ struct dirent * e;
+ char path[FILE_PATH_SIZE];
+ struct stat st;
+ FileAttrs attrs;
+ errno = 0;
+ e = readdir(h->dir);
+ if (e == NULL) {
+ err = errno;
+ if (err == 0) eof = 1;
+ break;
+ }
+ if (strcmp(e->d_name, ".") == 0) continue;
+ if (strcmp(e->d_name, "..") == 0) continue;
+ if (cnt > 0) write_stream(&c->out, ',');
+ write_stream(&c->out, '{');
+ json_write_string(&c->out, "FileName");
+ write_stream(&c->out, ':');
+ json_write_string(&c->out, e->d_name);
+ memset(&st, 0, sizeof(st));
+ snprintf(path, sizeof(path), "%s/%s", h->path, e->d_name);
+ if (stat(path, &st) == 0) {
+ fill_attrs(&attrs, &st);
+#if defined(WIN32)
+ attrs.win32_attrs = GetFileAttributes(path);
+#endif
+ write_stream(&c->out, ',');
+ json_write_string(&c->out, "Attrs");
+ write_stream(&c->out, ':');
+ write_file_attrs(&c->out, &attrs);
+ }
+ write_stream(&c->out, '}');
+ cnt++;
+ }
+ write_stream(&c->out, ']');
+ write_stream(&c->out, 0);
+ }
+
+ write_fs_errno(&c->out, err);
+ json_write_boolean(&c->out, eof);
+ write_stream(&c->out, 0);
+ write_stream(&c->out, MARKER_EOM);
+}
+
+static void command_remove(char * token, Channel * c) {
+ char path[FILE_PATH_SIZE];
+ int err = 0;
+
+ read_path(&c->inp, path, sizeof(path));
+ if (read_stream(&c->inp) != 0) exception(ERR_JSON_SYNTAX);
+ if (read_stream(&c->inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
+
+ if (remove(path) < 0) err = errno;
+
+ write_stringz(&c->out, "R");
+ write_stringz(&c->out, token);
+ write_fs_errno(&c->out, err);
+ write_stream(&c->out, MARKER_EOM);
+}
+
+static void command_rmdir(char * token, Channel * c) {
+ char path[FILE_PATH_SIZE];
+ int err = 0;
+
+ read_path(&c->inp, path, sizeof(path));
+ if (read_stream(&c->inp) != 0) exception(ERR_JSON_SYNTAX);
+ if (read_stream(&c->inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
+
+ if (rmdir(path) < 0) err = errno;
+
+ write_stringz(&c->out, "R");
+ write_stringz(&c->out, token);
+ write_fs_errno(&c->out, err);
+ write_stream(&c->out, MARKER_EOM);
+}
+
+static void command_mkdir(char * token, Channel * c) {
+ char path[FILE_PATH_SIZE];
+ FileAttrs attrs;
+ int err = 0;
+ int mode = 0777;
+
+ read_path(&c->inp, path, sizeof(path));
+ if (read_stream(&c->inp) != 0) exception(ERR_JSON_SYNTAX);
+ memset(&attrs, 0, sizeof(FileAttrs));
+#if defined(WIN32)
+ attrs.win32_attrs = INVALID_FILE_ATTRIBUTES;
+#endif
+ json_read_struct(&c->inp, read_file_attrs, &attrs);
+ if (read_stream(&c->inp) != 0) exception(ERR_JSON_SYNTAX);
+ if (read_stream(&c->inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
+
+ if (attrs.flags & ATTR_PERMISSIONS) {
+ mode = attrs.permissions;
+ }
+#if defined(_WRS_KERNEL)
+ if (mkdir(path) < 0) err = errno;
+#else
+ if (mkdir(path, mode) < 0) err = errno;
+#endif
+#if defined(WIN32)
+ if (attrs.win32_attrs != INVALID_FILE_ATTRIBUTES) {
+ if (SetFileAttributes(path, attrs.win32_attrs) == 0)
+ err = set_win32_errno(GetLastError());
+ }
+#endif
+
+ write_stringz(&c->out, "R");
+ write_stringz(&c->out, token);
+ write_fs_errno(&c->out, err);
+ write_stream(&c->out, MARKER_EOM);
+}
+
+static void command_realpath(char * token, Channel * c) {
+ char path[FILE_PATH_SIZE];
+ char * real = NULL;
+ int err = 0;
+
+ read_path(&c->inp, path, sizeof(path));
+ if (read_stream(&c->inp) != 0) exception(ERR_JSON_SYNTAX);
+ if (read_stream(&c->inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
+
+#if defined(__CYGWIN__)
+ if (strncmp(path, "/cygdrive/", 10) == 0) {
+ char buf[FILE_PATH_SIZE];
+ snprintf(buf, sizeof(buf), "%c:/%s", path[10], path + 12);
+ strlcpy(path, buf, sizeof(path));
+ }
+#endif
+
+ real = canonicalize_file_name(path);
+ if (real == NULL) err = errno;
+
+ write_stringz(&c->out, "R");
+ write_stringz(&c->out, token);
+ write_fs_errno(&c->out, err);
+ json_write_string(&c->out, real);
+ write_stream(&c->out, 0);
+ write_stream(&c->out, MARKER_EOM);
+ free(real);
+}
+
+static void command_rename(char * token, Channel * c) {
+ char path[FILE_PATH_SIZE];
+ char newp[FILE_PATH_SIZE];
+ int err = 0;
+
+ read_path(&c->inp, path, sizeof(path));
+ if (read_stream(&c->inp) != 0) exception(ERR_JSON_SYNTAX);
+ read_path(&c->inp, newp, sizeof(newp));
+ if (read_stream(&c->inp) != 0) exception(ERR_JSON_SYNTAX);
+ if (read_stream(&c->inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
+
+ if (rename(path, newp) < 0) err = errno;
+
+ write_stringz(&c->out, "R");
+ write_stringz(&c->out, token);
+ write_fs_errno(&c->out, err);
+ write_stream(&c->out, MARKER_EOM);
+}
+
+static void command_readlink(char * token, Channel * c) {
+ char path[FILE_PATH_SIZE];
+ char link[FILE_PATH_SIZE];
+ int err = 0;
+
+ read_path(&c->inp, path, sizeof(path));
+ if (read_stream(&c->inp) != 0) exception(ERR_JSON_SYNTAX);
+ if (read_stream(&c->inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
+
+ link[0] = 0;
+#if defined(WIN32) || defined(_WRS_KERNEL)
+ err = ENOSYS;
+#else
+ if (readlink(path, link, sizeof(link)) < 0) err = errno;
+#endif
+
+ write_stringz(&c->out, "R");
+ write_stringz(&c->out, token);
+ write_fs_errno(&c->out, err);
+ json_write_string(&c->out, link);
+ write_stream(&c->out, 0);
+ write_stream(&c->out, MARKER_EOM);
+}
+
+static void command_symlink(char * token, Channel * c) {
+ char link[FILE_PATH_SIZE];
+ char target[FILE_PATH_SIZE];
+ int err = 0;
+
+ read_path(&c->inp, link, sizeof(link));
+ if (read_stream(&c->inp) != 0) exception(ERR_JSON_SYNTAX);
+ read_path(&c->inp, target, sizeof(target));
+ if (read_stream(&c->inp) != 0) exception(ERR_JSON_SYNTAX);
+ if (read_stream(&c->inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
+
+#if defined(WIN32) || defined(_WRS_KERNEL)
+ err = ENOSYS;
+#else
+ if (symlink(target, link) < 0) err = errno;
+#endif
+
+ write_stringz(&c->out, "R");
+ write_stringz(&c->out, token);
+ write_fs_errno(&c->out, err);
+ write_stream(&c->out, MARKER_EOM);
+}
+
+static void command_copy(char * token, Channel * c) {
+ char src[FILE_PATH_SIZE];
+ char dst[FILE_PATH_SIZE];
+ int copy_uidgid;
+ int copy_perms;
+ struct stat st;
+ int fi = -1;
+ int fo = -1;
+ int err = 0;
+ int64_t pos = 0;
+
+ read_path(&c->inp, src, sizeof(src));
+ if (read_stream(&c->inp) != 0) exception(ERR_JSON_SYNTAX);
+ read_path(&c->inp, dst, sizeof(dst));
+ if (read_stream(&c->inp) != 0) exception(ERR_JSON_SYNTAX);
+ copy_uidgid = json_read_boolean(&c->inp);
+ if (read_stream(&c->inp) != 0) exception(ERR_JSON_SYNTAX);
+ copy_perms = json_read_boolean(&c->inp);
+ if (read_stream(&c->inp) != 0) exception(ERR_JSON_SYNTAX);
+ if (read_stream(&c->inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
+
+ if (stat(src, &st) < 0) err = errno;
+ if (err == 0 && (fi = open(src, O_RDONLY | O_BINARY, 0)) < 0) err = errno;
+ if (err == 0 && (fo = open(dst, O_WRONLY | O_BINARY | O_CREAT, 0775)) < 0) err = errno;
+
+ while (err == 0 && pos < st.st_size) {
+ char buf[BUF_SIZE];
+ ssize_t wr = 0;
+ ssize_t rd = read(fi, buf, sizeof(buf));
+ if (rd == 0) break;
+ if (rd < 0) {
+ err = errno;
+ break;
+ }
+ wr = write(fo, buf, rd);
+ if (wr < 0) {
+ err = errno;
+ break;
+ }
+ if (wr < rd) {
+ err = ENOSPC;
+ break;
+ }
+ pos += rd;
+ }
+
+ if (fo >= 0 && close(fo) < 0 && err == 0) err = errno;
+ if (fi >= 0 && close(fi) < 0 && err == 0) err = errno;
+
+ if (err == 0) {
+ struct utimbuf buf;
+ buf.actime = st.st_atime;
+ buf.modtime = st.st_mtime;
+ if (utime(dst, &buf) < 0) err = errno;
+ }
+ if (err == 0 && copy_perms && chmod(dst, st.st_mode) < 0) err = errno;
+#if !defined(WIN32) && !defined(_WRS_KERNEL)
+ if (err == 0 && copy_uidgid && chown(dst, st.st_uid, st.st_gid) < 0) err = errno;
+#endif
+
+ write_stringz(&c->out, "R");
+ write_stringz(&c->out, token);
+ write_fs_errno(&c->out, err);
+ write_stream(&c->out, MARKER_EOM);
+}
+
+static void command_user(char * token, Channel * c) {
+ if (read_stream(&c->inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
+ write_stringz(&c->out, "R");
+ write_stringz(&c->out, token);
+ json_write_long(&c->out, getuid());
+ write_stream(&c->out, 0);
+ json_write_long(&c->out, geteuid());
+ write_stream(&c->out, 0);
+ json_write_long(&c->out, getgid());
+ write_stream(&c->out, 0);
+ json_write_long(&c->out, getegid());
+ write_stream(&c->out, 0);
+ json_write_string(&c->out, get_user_home());
+ write_stream(&c->out, 0);
+
+ write_stream(&c->out, MARKER_EOM);
+}
+
+static void command_roots(char * token, Channel * c) {
+ struct stat st;
+ int err = 0;
+
+ if (read_stream(&c->inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
+ write_stringz(&c->out, "R");
+ write_stringz(&c->out, token);
+ write_stream(&c->out, '[');
+
+#ifdef WIN32
+ {
+ int cnt = 0;
+ int disk = 0;
+ DWORD disks = GetLogicalDrives();
+ for (disk = 0; disk <= 30; disk++) {
+ if (disks & (1 << disk)) {
+ char path[32];
+ snprintf(path, sizeof(path), "%c:\\", 'A' + disk);
+ if (cnt > 0) write_stream(&c->out, ',');
+ write_stream(&c->out, '{');
+ json_write_string(&c->out, "FileName");
+ write_stream(&c->out, ':');
+ json_write_string(&c->out, path);
+ if (disk >= 2) {
+ ULARGE_INTEGER total_number_of_bytes;
+ BOOL has_size = GetDiskFreeSpaceExA(path, NULL, &total_number_of_bytes, NULL);
+ memset(&st, 0, sizeof(st));
+#if defined(__CYGWIN__)
+ snprintf(path, sizeof(path), "/cygdrive/%c", 'a' + disk);
+#endif
+ if (has_size && stat(path, &st) == 0) {
+ FileAttrs attrs;
+ fill_attrs(&attrs, &st);
+ attrs.win32_attrs = GetFileAttributes(path);
+ write_stream(&c->out, ',');
+ json_write_string(&c->out, "Attrs");
+ write_stream(&c->out, ':');
+ write_file_attrs(&c->out, &attrs);
+ }
+ }
+ write_stream(&c->out, '}');
+ cnt++;
+ }
+ }
+ }
+#elif defined(_WRS_KERNEL)
+ {
+ extern DL_LIST iosDvList;
+ DEV_HDR * dev;
+ int cnt = 0;
+ for (dev = (DEV_HDR *)DLL_FIRST(&iosDvList); dev != NULL; dev = (DEV_HDR *)DLL_NEXT(&dev->node)) {
+ FileAttrs attrs;
+ char path[FILE_PATH_SIZE];
+ if (strcmp(dev->name, "host:") == 0) {
+ /* Windows host is special case */
+ int d;
+ for (d = 'a'; d < 'z'; d++) {
+ snprintf(path, sizeof(path), "%s%c:/", dev->name, d);
+ if (stat(path, &st) == 0 && S_ISDIR(st.st_mode)) {
+ fill_attrs(&attrs, &st);
+ if (cnt > 0) write_stream(&c->out, ',');
+ write_stream(&c->out, '{');
+ json_write_string(&c->out, "FileName");
+ write_stream(&c->out, ':');
+ json_write_string(&c->out, path);
+ write_stream(&c->out, ',');
+ json_write_string(&c->out, "Attrs");
+ write_stream(&c->out, ':');
+ write_file_attrs(&c->out, &attrs);
+ write_stream(&c->out, '}');
+ cnt++;
+ }
+ }
+ }
+ snprintf(path, sizeof(path), "%s/", dev->name);
+ if (stat(path, &st) == 0 && S_ISDIR(st.st_mode)) {
+ fill_attrs(&attrs, &st);
+ if (cnt > 0) write_stream(&c->out, ',');
+ write_stream(&c->out, '{');
+ json_write_string(&c->out, "FileName");
+ write_stream(&c->out, ':');
+ json_write_string(&c->out, path);
+ write_stream(&c->out, ',');
+ json_write_string(&c->out, "Attrs");
+ write_stream(&c->out, ':');
+ write_file_attrs(&c->out, &attrs);
+ write_stream(&c->out, '}');
+ cnt++;
+ }
+ }
+ }
+#else
+ write_stream(&c->out, '{');
+ json_write_string(&c->out, "FileName");
+ write_stream(&c->out, ':');
+ json_write_string(&c->out, "/");
+ memset(&st, 0, sizeof(st));
+ if (stat("/", &st) == 0) {
+ FileAttrs attrs;
+ fill_attrs(&attrs, &st);
+ write_stream(&c->out, ',');
+ json_write_string(&c->out, "Attrs");
+ write_stream(&c->out, ':');
+ write_file_attrs(&c->out, &attrs);
+ }
+ write_stream(&c->out, '}');
+#endif
+
+ write_stream(&c->out, ']');
+ write_stream(&c->out, 0);
+ write_fs_errno(&c->out, err);
+
+ write_stream(&c->out, MARKER_EOM);
+}
+
+void ini_file_system_service(Protocol * proto) {
+ int i;
+
+ add_channel_close_listener(channel_close_listener);
+ list_init(&file_info_ring);
+ for (i = 0; i < HANDLE_HASH_SIZE; i++) {
+ list_init(&handle_hash[i]);
+ }
+
+ add_command_handler(proto, FILE_SYSTEM, "open", command_open);
+ add_command_handler(proto, FILE_SYSTEM, "close", command_close);
+ add_command_handler(proto, FILE_SYSTEM, "read", command_read);
+ add_command_handler(proto, FILE_SYSTEM, "write", command_write);
+ add_command_handler(proto, FILE_SYSTEM, "stat", command_stat);
+ add_command_handler(proto, FILE_SYSTEM, "lstat", command_lstat);
+ add_command_handler(proto, FILE_SYSTEM, "fstat", command_fstat);
+ add_command_handler(proto, FILE_SYSTEM, "setstat", command_setstat);
+ add_command_handler(proto, FILE_SYSTEM, "fsetstat", command_fsetstat);
+ add_command_handler(proto, FILE_SYSTEM, "opendir", command_opendir);
+ add_command_handler(proto, FILE_SYSTEM, "readdir", command_readdir);
+ add_command_handler(proto, FILE_SYSTEM, "remove", command_remove);
+ add_command_handler(proto, FILE_SYSTEM, "rmdir", command_rmdir);
+ add_command_handler(proto, FILE_SYSTEM, "mkdir", command_mkdir);
+ add_command_handler(proto, FILE_SYSTEM, "realpath", command_realpath);
+ add_command_handler(proto, FILE_SYSTEM, "rename", command_rename);
+ add_command_handler(proto, FILE_SYSTEM, "readlink", command_readlink);
+ add_command_handler(proto, FILE_SYSTEM, "symlink", command_symlink);
+ add_command_handler(proto, FILE_SYSTEM, "copy", command_copy);
+ add_command_handler(proto, FILE_SYSTEM, "user", command_user);
+ add_command_handler(proto, FILE_SYSTEM, "roots", command_roots);
+}
+
+#endif /* SERVICE_FileSystem */
diff --git a/agent/tcf/services/filesystem.h b/agent/tcf/services/filesystem.h
new file mode 100644
index 00000000..98a846ce
--- /dev/null
+++ b/agent/tcf/services/filesystem.h
@@ -0,0 +1,29 @@
+/*******************************************************************************
+ * 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: file system access (TCF name FileSystem)
+ */
+
+#ifndef D_filesystem
+#define D_filesystem
+
+#include <framework/protocol.h>
+/*
+ * Initialize file system service.
+ */
+extern void ini_file_system_service(Protocol *);
+
+#endif
diff --git a/agent/tcf/services/linenumbers.c b/agent/tcf/services/linenumbers.c
new file mode 100644
index 00000000..7ed0d8e1
--- /dev/null
+++ b/agent/tcf/services/linenumbers.c
@@ -0,0 +1,267 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+
+/*
+ * TCF service line Numbers - common part.
+ *
+ * The service associates locations in the source files with the corresponding
+ * machine instruction addresses in the executable object.
+ */
+
+#include <config.h>
+
+#if SERVICE_LineNumbers
+
+#include <errno.h>
+#include <assert.h>
+#include <stdio.h>
+#include <framework/context.h>
+#include <framework/myalloc.h>
+#include <framework/exceptions.h>
+#include <framework/cache.h>
+#include <framework/json.h>
+#include <framework/protocol.h>
+#include <framework/trace.h>
+#include <services/linenumbers.h>
+
+#define MAX_AREA_CNT 256
+
+typedef struct MapToSourceArgs {
+ char token[256];
+ char id[256];
+ ContextAddress addr0;
+ ContextAddress addr1;
+} MapToSourceArgs;
+
+typedef struct MapToMemoryArgs {
+ char token[256];
+ char id[256];
+ char * file;
+ int line;
+ int column;
+} MapToMemoryArgs;
+
+static int code_area_cnt = 0;
+static int code_area_max = 0;
+static CodeArea * code_area_buf = NULL;
+
+static const char * LINENUMBERS = "LineNumbers";
+
+static void write_line_info(OutputStream * out, int cnt) {
+ CodeArea * area = code_area_buf + cnt;
+ CodeArea * prev = cnt == 0 ? NULL : code_area_buf + cnt - 1;
+
+ write_stream(out, '{');
+ json_write_string(out, "SAddr");
+ write_stream(out, ':');
+ json_write_uint64(out, area->start_address);
+ if (area->start_line > 0) {
+ write_stream(out, ',');
+ json_write_string(out, "SLine");
+ write_stream(out, ':');
+ json_write_ulong(out, area->start_line);
+ if (area->start_column > 0) {
+ write_stream(out, ',');
+ json_write_string(out, "SCol");
+ write_stream(out, ':');
+ json_write_ulong(out, area->start_column);
+ }
+ }
+ if (area->end_address != 0) {
+ write_stream(out, ',');
+ json_write_string(out, "EAddr");
+ write_stream(out, ':');
+ json_write_uint64(out, area->end_address);
+ }
+ if (area->end_line > 0) {
+ write_stream(out, ',');
+ json_write_string(out, "ELine");
+ write_stream(out, ':');
+ json_write_ulong(out, area->end_line);
+ if (area->end_column > 0) {
+ write_stream(out, ',');
+ json_write_string(out, "ECol");
+ write_stream(out, ':');
+ json_write_ulong(out, area->end_column);
+ }
+ }
+ if (area->file != NULL && (prev == NULL || prev->file != area->file)) {
+ write_stream(out, ',');
+ json_write_string(out, "File");
+ write_stream(out, ':');
+ json_write_string(out, area->file);
+ }
+ if (area->directory != NULL && (prev == NULL || prev->directory != area->directory)) {
+ write_stream(out, ',');
+ json_write_string(out, "Dir");
+ write_stream(out, ':');
+ json_write_string(out, area->directory);
+ }
+ if (area->isa > 0) {
+ write_stream(out, ',');
+ json_write_string(out, "ISA");
+ write_stream(out, ':');
+ json_write_ulong(out, area->isa);
+ }
+ if (area->is_statement) {
+ write_stream(out, ',');
+ json_write_string(out, "IsStmt");
+ write_stream(out, ':');
+ json_write_boolean(out, 1);
+ }
+ if (area->basic_block) {
+ write_stream(out, ',');
+ json_write_string(out, "BasicBlock");
+ write_stream(out, ':');
+ json_write_boolean(out, 1);
+ }
+ if (area->prologue_end) {
+ write_stream(out, ',');
+ json_write_string(out, "PrologueEnd");
+ write_stream(out, ':');
+ json_write_boolean(out, 1);
+ }
+ if (area->epilogue_begin) {
+ write_stream(out, ',');
+ json_write_string(out, "EpilogueBegin");
+ write_stream(out, ':');
+ json_write_boolean(out, 1);
+ }
+ write_stream(out, '}');
+}
+
+static void add_code_area(CodeArea * area, void * args) {
+ if (code_area_cnt >= code_area_max) {
+ if (code_area_max >= MAX_AREA_CNT) exception(ERR_BUFFER_OVERFLOW);
+ code_area_max += 8;
+ code_area_buf = (CodeArea *)loc_realloc(code_area_buf, sizeof(CodeArea) * code_area_max);
+ }
+ code_area_buf[code_area_cnt++] = *area;
+}
+
+static void map_to_source_cache_client(void * x) {
+ int err = 0;
+ Context * ctx = NULL;
+ MapToSourceArgs * args = (MapToSourceArgs *)x;
+ Channel * c = cache_channel();
+
+ ctx = id2ctx(args->id);
+ if (ctx == NULL) err = ERR_INV_CONTEXT;
+ else if (ctx->exited) err = ERR_ALREADY_EXITED;
+ else ctx = context_get_group(ctx, CONTEXT_GROUP_PROCESS);
+
+ code_area_cnt = 0;
+ if (err == 0 && address_to_line(ctx, args->addr0, args->addr1, add_code_area, NULL) < 0) err = errno;
+
+ cache_exit();
+
+ write_stringz(&c->out, "R");
+ write_stringz(&c->out, args->token);
+ write_errno(&c->out, err);
+ if (err != 0) {
+ write_stringz(&c->out, "null");
+ }
+ else {
+ int cnt = 0;
+ write_stream(&c->out, '[');
+ while (cnt < code_area_cnt) {
+ if (cnt > 0) write_stream(&c->out, ',');
+ write_line_info(&c->out, cnt);
+ cnt++;
+ }
+ write_stream(&c->out, ']');
+ write_stream(&c->out, 0);
+ }
+ write_stream(&c->out, MARKER_EOM);
+}
+
+static void command_map_to_source(char * token, Channel * c) {
+ MapToSourceArgs args;
+
+ json_read_string(&c->inp, args.id, sizeof(args.id));
+ if (read_stream(&c->inp) != 0) exception(ERR_JSON_SYNTAX);
+ args.addr0 = (ContextAddress)json_read_uint64(&c->inp);
+ if (read_stream(&c->inp) != 0) exception(ERR_JSON_SYNTAX);
+ args.addr1 = (ContextAddress)json_read_uint64(&c->inp);
+ 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(map_to_source_cache_client, c, &args, sizeof(args));
+}
+
+static void map_to_memory_cache_client(void * x) {
+ int err = 0;
+ Context * ctx = NULL;
+ MapToMemoryArgs * args = (MapToMemoryArgs *)x;
+ Channel * c = cache_channel();
+
+ ctx = id2ctx(args->id);
+ if (ctx == NULL) err = ERR_INV_CONTEXT;
+ else if (ctx->exited) err = ERR_ALREADY_EXITED;
+ else ctx = context_get_group(ctx, CONTEXT_GROUP_PROCESS);
+
+
+ code_area_cnt = 0;
+ if (err == 0 && line_to_address(ctx, args->file,
+ args->line, args->column, add_code_area, NULL) < 0) err = errno;
+
+ cache_exit();
+
+ write_stringz(&c->out, "R");
+ write_stringz(&c->out, args->token);
+ write_errno(&c->out, err);
+ if (err != 0) {
+ write_stringz(&c->out, "null");
+ }
+ else {
+ int cnt = 0;
+ write_stream(&c->out, '[');
+ while (cnt < code_area_cnt) {
+ if (cnt > 0) write_stream(&c->out, ',');
+ write_line_info(&c->out, cnt);
+ cnt++;
+ }
+ write_stream(&c->out, ']');
+ write_stream(&c->out, 0);
+ }
+ write_stream(&c->out, MARKER_EOM);
+ loc_free(args->file);
+}
+
+static void command_map_to_memory(char * token, Channel * c) {
+ MapToMemoryArgs args;
+
+ json_read_string(&c->inp, args.id, sizeof(args.id));
+ if (read_stream(&c->inp) != 0) exception(ERR_JSON_SYNTAX);
+ args.file = json_read_alloc_string(&c->inp);
+ if (read_stream(&c->inp) != 0) exception(ERR_JSON_SYNTAX);
+ args.line = json_read_long(&c->inp);
+ if (read_stream(&c->inp) != 0) exception(ERR_JSON_SYNTAX);
+ args.column = json_read_long(&c->inp);
+ 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(map_to_memory_cache_client, c, &args, sizeof(args));
+}
+
+void ini_line_numbers_service(Protocol * proto) {
+ ini_line_numbers_lib();
+ add_command_handler(proto, LINENUMBERS, "mapToSource", command_map_to_source);
+ add_command_handler(proto, LINENUMBERS, "mapToMemory", command_map_to_memory);
+}
+
+#endif /* SERVICE_LineNumbers */
diff --git a/agent/tcf/services/linenumbers.h b/agent/tcf/services/linenumbers.h
new file mode 100644
index 00000000..3e43c970
--- /dev/null
+++ b/agent/tcf/services/linenumbers.h
@@ -0,0 +1,59 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+
+/*
+ * TCF service Line Numbers
+ * The service associates locations in the source files with the corresponding
+ * machine instruction addresses in the executable object.
+ */
+
+#ifndef D_linenumbers
+#define D_linenumbers
+
+#include <framework/protocol.h>
+#include <framework/context.h>
+
+typedef struct CodeArea {
+ char * directory;
+ char * file;
+ uint32_t file_mtime;
+ uint32_t file_size;
+ ContextAddress start_address;
+ int start_line;
+ int start_column;
+ ContextAddress end_address;
+ int end_line;
+ int end_column;
+ int isa;
+ int is_statement;
+ int basic_block;
+ int prologue_end;
+ int epilogue_begin;
+} CodeArea;
+
+typedef void LineNumbersCallBack(CodeArea *, void *);
+
+extern int line_to_address(Context * ctx, char * file, int line, int column, LineNumbersCallBack * client, void * args);
+
+extern int address_to_line(Context * ctx, ContextAddress addr0, ContextAddress addr1, LineNumbersCallBack * client, void * args);
+
+/*
+ * Initialize Line Numbers service.
+ */
+extern void ini_line_numbers_service(Protocol *);
+extern void ini_line_numbers_lib(void);
+
+
+#endif /* D_linenumbers */
diff --git a/agent/tcf/services/linenumbers_elf.c b/agent/tcf/services/linenumbers_elf.c
new file mode 100644
index 00000000..96e305aa
--- /dev/null
+++ b/agent/tcf/services/linenumbers_elf.c
@@ -0,0 +1,305 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2011 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 - ELF version.
+ *
+ * The service associates locations in the source files with the corresponding
+ * machine instruction addresses in the executable object.
+ */
+
+#include <config.h>
+
+#if SERVICE_LineNumbers && !ENABLE_LineNumbersProxy && ENABLE_ELF
+
+#include <errno.h>
+#include <assert.h>
+#include <stdio.h>
+#include <framework/context.h>
+#include <framework/myalloc.h>
+#include <framework/exceptions.h>
+#include <framework/cache.h>
+#include <framework/trace.h>
+#include <framework/json.h>
+#include <framework/protocol.h>
+#include <services/linenumbers.h>
+#include <services/tcf_elf.h>
+#include <services/dwarfio.h>
+#include <services/dwarf.h>
+#include <services/dwarfcache.h>
+#include <services/stacktrace.h>
+#include <services/pathmap.h>
+
+static int is_absolute_path(char * fnm) {
+ if (fnm[0] == '/') return 1;
+ if (fnm[0] == '\\') return 1;
+ if (fnm[0] != 0 && fnm[1] == ':') {
+ if (fnm[2] == '/') return 1;
+ if (fnm[2] == '\\') return 1;
+ }
+ return 0;
+}
+
+static int compare_path(Channel * chnl, Context * ctx, char * file, char * pwd, char * dir, char * name) {
+ int i, j;
+ char buf[FILE_PATH_SIZE];
+ char * full_name = NULL;
+
+ if (file == NULL) return 0;
+ if (name == NULL) return 0;
+
+ if (is_absolute_path(name)) {
+ full_name = name;
+ }
+ else if (dir != NULL && is_absolute_path(dir)) {
+ snprintf(full_name = buf, sizeof(buf), "%s/%s", dir, name);
+ }
+ else if (dir != NULL && pwd != NULL) {
+ snprintf(full_name = buf, sizeof(buf), "%s/%s/%s", pwd, dir, name);
+ }
+ else if (pwd != NULL) {
+ snprintf(full_name = buf, sizeof(buf), "%s/%s", pwd, name);
+ }
+ else {
+ full_name = name;
+ }
+ full_name = canonic_path_map_file_name(full_name);
+#if SERVICE_PathMap
+ if (full_name != buf) strlcpy(buf, full_name, sizeof(buf));
+ full_name = apply_path_map(chnl, ctx, buf, PATH_MAP_TO_CLIENT);
+ if (full_name != buf) full_name = canonic_path_map_file_name(full_name);
+#endif
+ i = strlen(file);
+ j = strlen(full_name);
+ return i <= j && strcmp(file, full_name + j - i) == 0;
+}
+
+static LineNumbersState * get_next_in_text(CompUnit * unit, LineNumbersState * state) {
+ LineNumbersState * next = unit->mStates + state->mNext;
+ if (state->mNext == 0) return NULL;
+ while (next->mLine == state->mLine && next->mColumn == state->mColumn) {
+ if (next->mNext == 0) return NULL;
+ next = unit->mStates + next->mNext;
+ }
+ if (state->mFile != next->mFile) return NULL;
+ return next;
+}
+
+static void call_client(CompUnit * unit, LineNumbersState * state,
+ ContextAddress state_addr, LineNumbersCallBack * client, void * args) {
+ CodeArea area;
+ LineNumbersState * next = get_next_in_text(unit, state);
+ FileInfo * file_info = unit->mFiles + state->mFile;
+
+ if (state->mAddress >= (state + 1)->mAddress) return;
+ memset(&area, 0, sizeof(area));
+ area.start_line = state->mLine;
+ area.start_column = state->mColumn;
+ area.end_line = next ? next->mLine : state->mLine;
+ area.end_column = next ? next->mColumn : 0;
+
+ area.directory = unit->mDir;
+ if (state->mFileName != NULL) {
+ area.file = state->mFileName;
+ }
+ else if (is_absolute_path(file_info->mName) || file_info->mDir == NULL) {
+ area.file = file_info->mName;
+ }
+ else if (is_absolute_path(file_info->mDir)) {
+ area.directory = file_info->mDir;
+ area.file = file_info->mName;
+ }
+ else {
+ char buf[FILE_PATH_SIZE];
+ snprintf(buf, sizeof(buf), "%s/%s", file_info->mDir, file_info->mName);
+ area.file = state->mFileName = loc_strdup(buf);
+ }
+
+ area.file_mtime = file_info->mModTime;
+ area.file_size = file_info->mSize;
+ area.start_address = state_addr;
+ area.end_address = (state + 1)->mAddress - state->mAddress + state_addr;
+ area.isa = state->mISA;
+ area.is_statement = (state->mFlags & LINE_IsStmt) != 0;
+ area.basic_block = (state->mFlags & LINE_BasicBlock) != 0;
+ area.prologue_end = (state->mFlags & LINE_PrologueEnd) != 0;
+ area.epilogue_begin = (state->mFlags & LINE_EpilogueBegin) != 0;
+ client(&area, args);
+}
+
+static void unit_line_to_address(Context * ctx, CompUnit * unit, unsigned file, unsigned line, unsigned column,
+ LineNumbersCallBack * client, void * args) {
+ if (unit->mStatesCnt >= 2) {
+ unsigned l = 0;
+ unsigned h = unit->mStatesCnt - 1;
+ while (l < h) {
+ unsigned k = (h + l) / 2;
+ LineNumbersState * state = unit->mStatesIndex[k];
+ if (state->mFile < file) {
+ l = k + 1;
+ }
+ else if (state->mFile > file || state->mLine > line) {
+ h = k;
+ }
+ else {
+ LineNumbersState * next = get_next_in_text(unit, state);
+ if (next != NULL && next->mLine <= line) {
+ l = k + 1;
+ }
+ else {
+ unsigned i = k;
+ while (i > 0) {
+ LineNumbersState * prev = unit->mStatesIndex[i - 1];
+ if (prev->mFile != state->mFile) break;
+ if (prev->mLine != state->mLine) break;
+ if (prev->mColumn != state->mColumn) break;
+ state = prev;
+ i--;
+ }
+ for (;;) {
+ ContextAddress addr = elf_map_to_run_time_address(ctx, unit->mFile, unit->mTextSection, state->mAddress);
+ if (addr != 0) call_client(unit, state, addr, client, args);
+ if (i == k) break;
+ state = unit->mStatesIndex[++i];
+ }
+ break;
+ }
+ }
+ }
+ }
+}
+
+int line_to_address(Context * ctx, char * file_name, int line, int column,
+ LineNumbersCallBack * client, void * args) {
+ int err = 0;
+ Channel * chnl = cache_channel();
+
+ if (ctx == NULL) err = ERR_INV_CONTEXT;
+ else if (ctx->exited) err = ERR_ALREADY_EXITED;
+
+ if (err == 0) {
+ ELF_File * file = elf_list_first(ctx, 0, ~(ContextAddress)0);
+ if (file == NULL) err = errno;
+ if (err == 0) {
+ unsigned h;
+ char fnm[FILE_PATH_SIZE];
+ strlcpy(fnm, canonic_path_map_file_name(file_name), sizeof(fnm));
+ h = calc_file_name_hash(fnm);
+ while (file != NULL) {
+ Trap trap;
+ /* TODO: support for separate debug info files */
+ if (set_trap(&trap)) {
+ DWARFCache * cache = get_dwarf_cache(file);
+ ObjectInfo * info = cache->mCompUnits;
+ while (info != NULL) {
+ CompUnit * unit = info->mCompUnit;
+ if (!unit->mLineInfoLoaded) load_line_numbers(unit);
+ info = info->mSibling;
+ }
+ if (cache->mFileInfoHash) {
+ FileInfo * f = cache->mFileInfoHash[h % cache->mFileInfoHashSize];
+ while (f != NULL) {
+ if (f->mNameHash == h && compare_path(chnl, ctx, fnm, f->mCompUnit->mDir, f->mDir, f->mName)) {
+ CompUnit * unit = f->mCompUnit;
+ unsigned j = f - unit->mFiles;
+ unit_line_to_address(ctx, unit, j, line, column, client, args);
+ }
+ f = f->mNextInHash;
+ }
+ }
+ clear_trap(&trap);
+ }
+ else {
+ err = trap.error;
+ break;
+ }
+ file = elf_list_next(ctx);
+ if (file == NULL) err = errno;
+ }
+ }
+ elf_list_done(ctx);
+ }
+
+ if (err != 0) {
+ errno = err;
+ return -1;
+ }
+ return 0;
+}
+
+int address_to_line(Context * ctx, ContextAddress addr0, ContextAddress addr1, LineNumbersCallBack * client, void * args) {
+ Trap trap;
+
+ if (!set_trap(&trap)) return -1;
+ if (ctx == NULL) exception(ERR_INV_CONTEXT);
+ if (ctx->exited) exception(ERR_ALREADY_EXITED);
+ while (addr0 < addr1) {
+ ContextAddress range_rt_addr = 0;
+ UnitAddressRange * range = elf_find_unit(ctx, addr0, addr1, &range_rt_addr);
+ if (range == NULL) break;
+ assert(range_rt_addr != 0);
+ if (!range->mUnit->mLineInfoLoaded) load_line_numbers(range->mUnit);
+ if (range->mUnit->mStatesCnt >= 2) {
+ CompUnit * unit = range->mUnit;
+ unsigned l = 0;
+ unsigned h = unit->mStatesCnt - 1;
+ ContextAddress addr_min = addr0 - range_rt_addr + range->mAddr;
+ ContextAddress addr_max = addr1 - range_rt_addr + range->mAddr;
+ if (addr_min < range->mAddr) addr_min = range->mAddr;
+ if (addr_max > range->mAddr + range->mSize) addr_max = range->mAddr + range->mSize;
+ while (l < h) {
+ unsigned k = (h + l) / 2;
+ LineNumbersState * state = unit->mStates + k;
+ if (state->mAddress >= addr_max) {
+ h = k;
+ }
+ else {
+ LineNumbersState * next = state + 1;
+ assert(next->mAddress >= state->mAddress);
+ if (next->mAddress <= addr_min) {
+ l = k + 1;
+ }
+ else {
+ while (k > 0) {
+ LineNumbersState * prev = unit->mStates + k - 1;
+ if (state->mAddress <= addr_min) break;
+ if (prev->mAddress >= addr_max) break;
+ next = state;
+ state = prev;
+ k--;
+ }
+ for (;;) {
+ call_client(unit, state, state->mAddress - range->mAddr + range_rt_addr, client, args);
+ k++;
+ if (k >= unit->mStatesCnt - 1) break;
+ state = unit->mStates + k;
+ if (state->mAddress >= addr_max) break;
+ next = state + 1;
+ }
+ break;
+ }
+ }
+ }
+ }
+ addr0 = range_rt_addr + range->mSize;
+ }
+ clear_trap(&trap);
+ return 0;
+}
+
+void ini_line_numbers_lib(void) {
+}
+
+#endif /* SERVICE_LineNumbers && !ENABLE_LineNumbersProxy && ENABLE_ELF */
diff --git a/agent/tcf/services/linenumbers_proxy.c b/agent/tcf/services/linenumbers_proxy.c
new file mode 100644
index 00000000..3f920727
--- /dev/null
+++ b/agent/tcf/services/linenumbers_proxy.c
@@ -0,0 +1,316 @@
+/*******************************************************************************
+ * 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 */
diff --git a/agent/tcf/services/linenumbers_win32.c b/agent/tcf/services/linenumbers_win32.c
new file mode 100644
index 00000000..60b58747
--- /dev/null
+++ b/agent/tcf/services/linenumbers_win32.c
@@ -0,0 +1,207 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+
+/*
+ * TCF service line Numbers
+ * The service associates locations in the source files with the corresponding
+ * machine instruction addresses in the executable object.
+ */
+
+#include <config.h>
+
+#if SERVICE_LineNumbers && !ENABLE_LineNumbersProxy && defined(WIN32) && !ENABLE_ELF
+
+#include <errno.h>
+#include <assert.h>
+#include <stdio.h>
+#include <framework/json.h>
+#include <framework/protocol.h>
+#include <framework/context.h>
+#include <framework/exceptions.h>
+#include <services/linenumbers.h>
+#include <system/Windows/windbgcache.h>
+#include <system/Windows/context-win32.h>
+
+int line_to_address(Context * ctx, char * file, int line, int column,
+ LineNumbersCallBack * callback, void * user_args) {
+ int err = 0;
+
+ if (ctx == NULL) err = ERR_INV_CONTEXT;
+ else if (ctx->exited) err = ERR_ALREADY_EXITED;
+
+ if (err == 0 && ctx->parent != NULL) ctx = ctx->parent;
+
+ if (err == 0) {
+ LONG offset = 0;
+ IMAGEHLP_LINE img_line;
+ CodeArea area;
+ memset(&img_line, 0, sizeof(img_line));
+ memset(&area, 0, sizeof(area));
+ img_line.SizeOfStruct = sizeof(IMAGEHLP_LINE);
+
+ if (!SymGetLineFromName(get_context_handle(ctx), NULL, file, line, &offset, &img_line)) {
+ DWORD win_err = GetLastError();
+ if (win_err != ERROR_NOT_FOUND) {
+ err = set_win32_errno(win_err);
+ }
+ }
+ else {
+ IMAGEHLP_LINE img_next;
+ memcpy(&img_next, &img_line, sizeof(img_next));
+ if (!SymGetLineNext(get_context_handle(ctx), &img_next)) {
+ err = set_win32_errno(GetLastError());
+ }
+ else {
+ area.file = img_line.FileName;
+ area.start_line = img_line.LineNumber;
+ area.start_address = img_line.Address;
+ area.end_line = img_next.LineNumber;
+ area.end_address = img_next.Address;
+ callback(&area, user_args);
+ }
+ }
+ }
+
+ if (err != 0) {
+ errno = err;
+ return -1;
+ }
+ return 0;
+}
+
+#define JMPD08 0xeb
+#define JMPD32 0xe9
+#define GRP5 0xff
+#define JMPN 0x25
+
+int address_to_line(Context * ctx, ContextAddress addr0, ContextAddress addr1, LineNumbersCallBack * callback, void * user_args) {
+ int err = 0;
+ int not_found = 0;
+ DWORD offset = 0;
+ IMAGEHLP_LINE line;
+ IMAGEHLP_LINE next;
+ ContextAddress org_addr0 = addr0;
+ ContextAddress org_addr1 = addr1;
+
+ if (ctx == NULL) err = ERR_INV_CONTEXT;
+ else if (ctx->exited) err = ERR_ALREADY_EXITED;
+
+ if (err == 0 && ctx->parent != NULL) ctx = ctx->parent;
+
+ memset(&line, 0, sizeof(line));
+ line.SizeOfStruct = sizeof(IMAGEHLP_LINE);
+ if (addr0 >= addr1) not_found = 1;
+
+ while (err == 0 && not_found == 0 && !SymGetLineFromAddr(get_context_handle(ctx), addr0, &offset, &line)) {
+ DWORD w = GetLastError();
+ if (w == ERROR_MOD_NOT_FOUND) {
+ not_found = 1;
+ }
+ else if (w == ERROR_INVALID_ADDRESS) {
+ /* Check if the address points to a jump instruction (e.g. inside a jump table)
+ * and try to get line info for jump destination address.
+ */
+ unsigned char instr; /* instruction opcode at <addr0> */
+ ContextAddress dest = 0; /* Jump destination address */
+ if (context_read_mem(ctx, addr0, &instr, 1) == 0) {
+ /* If instruction is a JMP, get destination adrs */
+ if (instr == JMPD08) {
+ signed char disp08;
+ if (context_read_mem(ctx, addr0 + 1, &disp08, 1) == 0) {
+ dest = addr0 + 2 + disp08;
+ org_addr1 = addr0 + 2;
+ }
+ }
+ else if (instr == JMPD32) {
+ int disp32;
+ assert(sizeof(disp32) == 4);
+ if (context_read_mem(ctx, addr0 + 1, &disp32, 4) == 0) {
+ dest = addr0 + 5 + disp32;
+ org_addr1 = addr0 + 5;
+ }
+ }
+ else if (instr == GRP5) {
+ if (context_read_mem(ctx, addr0 + 1, &instr, 1) == 0 && instr == JMPN) {
+ ContextAddress ptr = 0;
+ if (context_read_mem(ctx, addr0 + 2, &ptr, 4) == 0) {
+ context_read_mem(ctx, ptr, &dest, 4);
+ org_addr1 = addr0 + 6;
+ }
+ }
+ }
+ }
+ if (dest != 0) {
+ addr0 = dest;
+ addr1 = dest + 1;
+ }
+ else {
+ not_found = 1;
+ }
+ }
+ else {
+ err = set_win32_errno(w);
+ }
+ }
+ memcpy(&next, &line, sizeof(next));
+ if (err == 0 && !not_found && !SymGetLineNext(get_context_handle(ctx), &next)) {
+ DWORD w = GetLastError();
+ if (w == ERROR_NOT_FOUND) {
+ /* Last line in the source file */
+ ULONG64 buffer[(sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(TCHAR) + sizeof(ULONG64) - 1) / sizeof(ULONG64)];
+ SYMBOL_INFO * info = (SYMBOL_INFO *)buffer;
+ info->SizeOfStruct = sizeof(SYMBOL_INFO);
+ info->MaxNameLen = MAX_SYM_NAME;
+ if (SymFromAddr(get_context_handle(ctx), next.Address, NULL, info)) {
+ next.Address = (ULONG_PTR)info->Address + info->Size;
+ next.LineNumber++;
+ }
+ }
+ else {
+ err = set_win32_errno(GetLastError());
+ }
+ }
+
+ if (err == 0 && !not_found) {
+ while (line.Address < next.Address && line.Address < addr1 && next.Address > addr0) {
+ CodeArea area;
+
+ memset(&area, 0, sizeof(area));
+ area.file = line.FileName;
+ area.start_address = line.Address;
+ area.start_line = line.LineNumber;
+ area.end_address = next.Address;
+ area.end_line = next.LineNumber;
+ if (org_addr0 != addr0) {
+ area.start_address = org_addr0;
+ area.end_address = org_addr1;
+ }
+ callback(&area, user_args);
+ memcpy(&line, &next, sizeof(line));
+ if (!SymGetLineNext(get_context_handle(ctx), &next)) break;
+ }
+ }
+
+ if (err != 0) {
+ errno = err;
+ return -1;
+ }
+ return 0;
+}
+
+void ini_line_numbers_lib(void) {
+ SymSetOptions(SymGetOptions() | SYMOPT_LOAD_LINES | SYMOPT_DEFERRED_LOADS);
+}
+
+#endif /* SERVICE_LineNumbers && !ENABLE_LineNumbersProxy && defined(_MSC_VER) && !ENABLE_ELF */
diff --git a/agent/tcf/services/memorymap.c b/agent/tcf/services/memorymap.c
new file mode 100644
index 00000000..f96a07a8
--- /dev/null
+++ b/agent/tcf/services/memorymap.c
@@ -0,0 +1,543 @@
+/*******************************************************************************
+ * 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.
+ * You may elect to redistribute this code under either of these licenses.
+ *
+ * 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 unsigned find_maps(LINK * maps, const char * id) {
+ LINK * l;
+ unsigned cnt = 0;
+ unsigned 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 (list_is_empty(&m->link_ctx) && strcmp(m->id, id) == 0) {
+ list_add_last(&m->link_ctx, maps);
+ cnt += m->map.region_cnt;
+ }
+ }
+ return cnt;
+}
+
+static Context * get_mem_context(Context * ctx) {
+#if ENABLE_DebugContext
+ ctx = context_get_group(ctx, CONTEXT_GROUP_PROCESS);
+#endif
+ return ctx;
+}
+
+static Context * get_sym_context(Context * ctx) {
+#if ENABLE_DebugContext
+ ctx = context_get_group(ctx, CONTEXT_GROUP_SYMBOLS);
+#endif
+ return ctx;
+}
+
+static void update_context_client_map(Context * ctx) {
+ ContextExtensionMM * ext = EXT(ctx);
+ Context * syms = get_sym_context(ctx);
+ unsigned r_cnt = 0;
+ int equ = 0;
+ unsigned i;
+ LINK * l;
+ LINK maps;
+
+ if (syms == NULL) return;
+ assert(ctx == get_mem_context(ctx));
+ list_init(&maps);
+ r_cnt += find_maps(&maps, syms->id);
+ if (syms->name != NULL) {
+ r_cnt += find_maps(&maps, syms->name);
+ r_cnt += find_maps(&maps, context_full_name(syms));
+ }
+ 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);
+ }
+ }
+ assert(!equ || k == r_cnt);
+ }
+ if (!equ) {
+ 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);
+ }
+ }
+ assert(ext->client_map.region_cnt == r_cnt);
+ }
+ while (!list_is_empty(&maps)) list_remove(maps.next);
+ if (!equ) memory_map_event_mapping_changed(ctx);
+}
+
+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 != get_mem_context(ctx)) continue;
+ update_context_client_map(ctx);
+ }
+}
+
+static void event_memory_map_changed(Context * ctx, void * args) {
+ OutputStream * out;
+ ContextExtensionMM * ext = EXT(ctx);
+
+ if (ctx->exited) return;
+ if (!ext->valid) return;
+ if (ctx != get_mem_context(ctx)) return;
+
+ context_clear_memory_map(&ext->target_map);
+ ext->valid = 0;
+
+ 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_changed(Context * ctx, void * args) {
+ if (ctx->exited) return;
+ if (ctx != get_mem_context(ctx)) return;
+ update_context_client_map(ctx);
+}
+
+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 == get_mem_context(ctx));
+#if ENABLE_DebugContext
+ 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;
+ }
+ }
+#endif
+ 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 == get_mem_context(ctx));
+ 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 == get_mem_context(ctx));
+ 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 == get_mem_context(ctx));
+ 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 memory_map_event_mapping_changed(Context * ctx) {
+ unsigned i;
+ assert(ctx->ref_count > 0);
+ assert(ctx == get_mem_context(ctx));
+ event_memory_map_changed(ctx, NULL);
+ for (i = 0; i < listener_cnt; i++) {
+ Listener * l = listeners + i;
+ if (l->listener->mapping_changed == NULL) continue;
+ l->listener->mapping_changed(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 = get_mem_context(ctx);
+
+ 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_context_changed,
+ NULL,
+ NULL,
+ NULL,
+ event_context_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
diff --git a/agent/tcf/services/memorymap.h b/agent/tcf/services/memorymap.h
new file mode 100644
index 00000000..34d74b61
--- /dev/null
+++ b/agent/tcf/services/memorymap.h
@@ -0,0 +1,70 @@
+/*******************************************************************************
+ * 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.
+ * You may elect to redistribute this code under either of these licenses.
+ *
+ * Contributors:
+ * Wind River Systems - initial API and implementation
+ *******************************************************************************/
+
+/*
+ * This module holds execution context memory maps.
+ */
+#ifndef D_memorymap
+#define D_memorymap
+
+#include <config.h>
+#include <framework/context.h>
+#include <framework/protocol.h>
+
+#if SERVICE_MemoryMap
+
+/*
+ * Get memory maps for given context.
+ * 'client_map' returns map entries that are created by the agent clients.
+ * 'target_map' returns map entries that the agent has found on a target.
+ * Return -1 and set errno if the context memory map cannot be retrieved.
+ * Return 0 on success.
+ */
+extern int memory_map_get(Context * ctx, MemoryMap ** client_map, MemoryMap ** target_map);
+
+/*
+ * Functions that are used by context implementation to notify memory map services about map changes.
+ */
+extern void memory_map_event_module_loaded(Context * ctx);
+extern void memory_map_event_code_section_ummapped(Context * ctx, ContextAddress addr, ContextAddress size);
+extern void memory_map_event_module_unloaded(Context * ctx);
+extern void memory_map_event_mapping_changed(Context * ctx);
+
+/*
+ * Memory map listener.
+ */
+typedef struct MemoryMapEventListener {
+ void (*module_loaded)(Context * ctx, void * client_data);
+ void (*code_section_ummapped)(Context * ctx, ContextAddress addr, ContextAddress size, void * client_data);
+ void (*module_unloaded)(Context * ctx, void * client_data);
+ void (*mapping_changed)(Context * ctx, void * client_data);
+} MemoryMapEventListener;
+
+/*
+ * Add memory map listener.
+ */
+extern void add_memory_map_event_listener(MemoryMapEventListener * listener, void * client_data);
+
+extern void ini_memory_map_service(Protocol * proto, TCFBroadcastGroup * bcg);
+
+#else
+
+#define memory_map_event_module_loaded(ctx)
+#define memory_map_event_code_section_ummapped(ctx, addr, size)
+#define memory_map_event_module_unloaded(ctx)
+#define memory_map_event_mapping_changed(ctx)
+
+#endif /* SERVICE_MemoryMap */
+#endif /* D_memorymap */
diff --git a/agent/tcf/services/memoryservice.c b/agent/tcf/services/memoryservice.c
new file mode 100644
index 00000000..b1847d16
--- /dev/null
+++ b/agent/tcf/services/memoryservice.c
@@ -0,0 +1,700 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+
+/*
+ * TCF Memory - memory access service.
+ */
+
+#include <config.h>
+
+#if SERVICE_Memory
+
+#include <assert.h>
+#include <framework/protocol.h>
+#include <framework/context.h>
+#include <framework/json.h>
+#include <framework/exceptions.h>
+#include <framework/myalloc.h>
+#include <framework/channel.h>
+#include <framework/trace.h>
+#include <services/memoryservice.h>
+#include <services/runctrl.h>
+
+static const char * MEMORY = "Memory";
+
+static TCFBroadcastGroup * broadcast_group = NULL;
+
+#define BYTE_VALID 0x00
+#define BYTE_UNKNOWN 0x01
+#define BYTE_INVALID 0x02
+#define BYTE_CANNOT_READ 0x04
+#define BYTE_CANNOT_WRITE 0x08
+
+#define CMD_GET 1
+#define CMD_SET 2
+#define CMD_FILL 3
+
+#define BUF_SIZE (128 * MEM_USAGE_FACTOR)
+
+typedef struct MemoryCommandArgs {
+ Channel * c;
+ char token[256];
+ ContextAddress addr;
+ unsigned long size;
+ int word_size;
+ int mode;
+ Context * ctx;
+} MemoryCommandArgs;
+
+static void write_context(OutputStream * out, Context * ctx) {
+ assert(!ctx->exited);
+
+ write_stream(out, '{');
+
+ json_write_string(out, "ID");
+ write_stream(out, ':');
+ json_write_string(out, ctx->id);
+
+ if (ctx->parent != NULL) {
+ write_stream(out, ',');
+ json_write_string(out, "ParentID");
+ write_stream(out, ':');
+ json_write_string(out, ctx->parent->id);
+ }
+
+ write_stream(out, ',');
+ json_write_string(out, "ProcessID");
+ write_stream(out, ':');
+ json_write_string(out, context_get_group(ctx, CONTEXT_GROUP_PROCESS)->id);
+
+ if (ctx->name != NULL) {
+ write_stream(out, ',');
+ json_write_string(out, "Name");
+ write_stream(out, ':');
+ json_write_string(out, ctx->name);
+ }
+
+ write_stream(out, ',');
+ json_write_string(out, "BigEndian");
+ write_stream(out, ':');
+ json_write_boolean(out, ctx->big_endian);
+
+ if (ctx->mem_access) {
+ int cnt = 0;
+
+ write_stream(out, ',');
+ json_write_string(out, "AddressSize");
+ write_stream(out, ':');
+ json_write_ulong(out, context_word_size(ctx));
+
+ write_stream(out, ',');
+ json_write_string(out, "AccessTypes");
+ write_stream(out, ':');
+ write_stream(out, '[');
+ if (ctx->mem_access & MEM_ACCESS_INSTRUCTION) {
+ if (cnt++) write_stream(out, ',');
+ json_write_string(out, "instruction");
+ }
+ if (ctx->mem_access & MEM_ACCESS_DATA) {
+ if (cnt++) write_stream(out, ',');
+ json_write_string(out, "data");
+ }
+ if (ctx->mem_access & MEM_ACCESS_IO) {
+ if (cnt++) write_stream(out, ',');
+ json_write_string(out, "io");
+ }
+ if (ctx->mem_access & MEM_ACCESS_USER) {
+ if (cnt++) write_stream(out, ',');
+ json_write_string(out, "user");
+ }
+ if (ctx->mem_access & MEM_ACCESS_SUPERVISOR) {
+ if (cnt++) write_stream(out, ',');
+ json_write_string(out, "supervisor");
+ }
+ if (ctx->mem_access & MEM_ACCESS_HYPERVISOR) {
+ if (cnt++) write_stream(out, ',');
+ json_write_string(out, "hypervisor");
+ }
+ if (ctx->mem_access & MEM_ACCESS_VIRTUAL) {
+ if (cnt++) write_stream(out, ',');
+ json_write_string(out, "virtual");
+ }
+ if (ctx->mem_access & MEM_ACCESS_PHYSICAL) {
+ if (cnt++) write_stream(out, ',');
+ json_write_string(out, "physical");
+ }
+ if (ctx->mem_access & MEM_ACCESS_CACHE) {
+ if (cnt++) write_stream(out, ',');
+ json_write_string(out, "cache");
+ }
+ if (ctx->mem_access & MEM_ACCESS_TLB) {
+ if (cnt++) write_stream(out, ',');
+ json_write_string(out, "tlb");
+ }
+ write_stream(out, ']');
+ }
+
+ write_stream(out, '}');
+}
+
+static void write_ranges(OutputStream * out, ContextAddress addr, int offs, int status, MemoryErrorInfo * err_info) {
+ int cnt = 0;
+ size_t size_valid = 0;
+ size_t size_error = 0;
+
+ if (err_info->error) {
+ size_valid = err_info->size_valid + offs;
+ size_error = err_info->size_error;
+ }
+ else {
+ size_valid = offs;
+ }
+
+ write_stream(out, '[');
+ if (size_valid > 0) {
+ write_stream(out, '{');
+
+ json_write_string(out, "addr");
+ write_stream(out, ':');
+ json_write_uint64(out, addr);
+ write_stream(out, ',');
+
+ json_write_string(out, "size");
+ write_stream(out, ':');
+ json_write_ulong(out, size_valid);
+ write_stream(out, ',');
+
+ json_write_string(out, "stat");
+ write_stream(out, ':');
+ json_write_ulong(out, 0);
+
+ write_stream(out, '}');
+ cnt++;
+ }
+ if (size_error > 0) {
+ if (cnt > 0) write_stream(out, ',');
+ write_stream(out, '{');
+
+ json_write_string(out, "addr");
+ write_stream(out, ':');
+ json_write_uint64(out, addr + size_valid);
+ write_stream(out, ',');
+
+ json_write_string(out, "size");
+ write_stream(out, ':');
+ json_write_ulong(out, size_error);
+ write_stream(out, ',');
+
+ json_write_string(out, "stat");
+ write_stream(out, ':');
+ json_write_ulong(out, BYTE_INVALID | status);
+ write_stream(out, ',');
+
+ json_write_string(out, "msg");
+ write_stream(out, ':');
+ write_error_object(out, err_info->error);
+
+ write_stream(out, '}');
+ }
+ write_stream(out, ']');
+ write_stream(out, 0);
+}
+
+static void command_get_context(char * token, Channel * c) {
+ int err = 0;
+ char id[256];
+ Context * ctx = 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 || ctx->mem_access == 0) err = ERR_INV_CONTEXT;
+ else if (ctx->exited) err = ERR_ALREADY_EXITED;
+
+ write_stringz(&c->out, "R");
+ write_stringz(&c->out, token);
+ write_errno(&c->out, err);
+ if (err == 0) {
+ write_context(&c->out, ctx);
+ }
+ else {
+ write_string(&c->out, "null");
+ }
+ write_stream(&c->out, 0);
+ write_stream(&c->out, MARKER_EOM);
+}
+
+static void command_get_children(char * token, Channel * c) {
+ char id[256];
+
+ 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);
+
+ write_stringz(&c->out, "R");
+ write_stringz(&c->out, token);
+
+ write_errno(&c->out, 0);
+
+ write_stream(&c->out, '[');
+ if (id[0] == 0) {
+ LINK * qp;
+ int cnt = 0;
+ for (qp = context_root.next; qp != &context_root; qp = qp->next) {
+ Context * ctx = ctxl2ctxp(qp);
+ if (ctx->parent != NULL) continue;
+ if (ctx->exited) continue;
+ if (ctx->mem_access == 0 && list_is_empty(&ctx->children)) continue;
+ if (cnt > 0) write_stream(&c->out, ',');
+ json_write_string(&c->out, ctx->id);
+ cnt++;
+ }
+ }
+ else {
+ Context * parent = id2ctx(id);
+ if (parent != NULL) {
+ LINK * l;
+ int cnt = 0;
+ for (l = parent->children.next; l != &parent->children; l = l->next) {
+ Context * ctx = cldl2ctxp(l);
+ assert(ctx->parent == parent);
+ if (ctx->exited) continue;
+ if (ctx->mem_access == 0 && list_is_empty(&ctx->children)) continue;
+ if (cnt > 0) write_stream(&c->out, ',');
+ json_write_string(&c->out, ctx->id);
+ cnt++;
+ }
+ }
+ }
+ write_stream(&c->out, ']');
+ write_stream(&c->out, 0);
+
+ write_stream(&c->out, MARKER_EOM);
+}
+
+static MemoryCommandArgs * read_command_args(char * token, Channel * c, int cmd) {
+ int err = 0;
+ char id[256];
+ MemoryCommandArgs buf;
+ memset(&buf, 0, sizeof(buf));
+
+ json_read_string(&c->inp, id, sizeof(id));
+ if (read_stream(&c->inp) != 0) exception(ERR_JSON_SYNTAX);
+ buf.addr = json_read_ulong(&c->inp);
+ if (read_stream(&c->inp) != 0) exception(ERR_JSON_SYNTAX);
+ buf.word_size = (int)json_read_long(&c->inp);
+ if (read_stream(&c->inp) != 0) exception(ERR_JSON_SYNTAX);
+ buf.size = (int)json_read_long(&c->inp);
+ if (read_stream(&c->inp) != 0) exception(ERR_JSON_SYNTAX);
+ buf.mode = (int)json_read_long(&c->inp);
+ if (read_stream(&c->inp) != 0) exception(ERR_JSON_SYNTAX);
+ if (cmd == CMD_GET && read_stream(&c->inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
+
+ buf.ctx = id2ctx(id);
+ if (buf.ctx == NULL) err = ERR_INV_CONTEXT;
+ else if (buf.ctx->exited) err = ERR_ALREADY_EXITED;
+ else if (buf.ctx->mem_access == 0) err = ERR_INV_CONTEXT;
+
+ if (err != 0) {
+ if (cmd != CMD_GET) {
+ int ch;
+ while ((ch = read_stream(&c->inp)) != 0) {
+ if (ch < 0) exception(ERR_JSON_SYNTAX);
+ }
+ if (read_stream(&c->inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
+ }
+
+ write_stringz(&c->out, "R");
+ write_stringz(&c->out, token);
+ if (cmd == CMD_GET) write_stringz(&c->out, "null");
+ write_errno(&c->out, err);
+ write_stringz(&c->out, "null");
+ write_stream(&c->out, MARKER_EOM);
+ return NULL;
+ }
+ else {
+ MemoryCommandArgs * args = (MemoryCommandArgs *)loc_alloc(sizeof(MemoryCommandArgs));
+ *args = buf;
+ args->c = c;
+ strlcpy(args->token, token, sizeof(args->token));
+ channel_lock(c);
+ context_lock(buf.ctx);
+ return args;
+ }
+}
+
+void send_event_memory_changed(Context * ctx, ContextAddress addr, unsigned long size) {
+ OutputStream * out = &broadcast_group->out;
+
+ write_stringz(out, "E");
+ write_stringz(out, MEMORY);
+ write_stringz(out, "memoryChanged");
+
+ json_write_string(out, ctx->id);
+ write_stream(out, 0);
+
+ /* <array of addres ranges> */
+ write_stream(out, '[');
+ write_stream(out, '{');
+
+ json_write_string(out, "addr");
+ write_stream(out, ':');
+ json_write_uint64(out, addr);
+
+ write_stream(out, ',');
+
+ json_write_string(out, "size");
+ write_stream(out, ':');
+ json_write_ulong(out, size);
+
+ write_stream(out, '}');
+ write_stream(out, ']');
+ write_stream(out, 0);
+
+ write_stream(out, MARKER_EOM);
+}
+
+static void safe_memory_set(void * parm) {
+ MemoryCommandArgs * args = (MemoryCommandArgs *)parm;
+ Channel * c = args->c;
+ Context * ctx = args->ctx;
+
+ if (!is_channel_closed(c)) {
+ Trap trap;
+ if (set_trap(&trap)) {
+ InputStream * inp = &c->inp;
+ OutputStream * out = &c->out;
+ char * token = args->token;
+ ContextAddress addr0 = args->addr;
+ ContextAddress addr = args->addr;
+ unsigned long size = 0;
+ char buf[BUF_SIZE];
+ int err = 0;
+ MemoryErrorInfo err_info;
+ JsonReadBinaryState state;
+
+ memset(&err_info, 0, sizeof(err_info));
+ if (ctx->exiting || ctx->exited) err = ERR_ALREADY_EXITED;
+
+ json_read_binary_start(&state, inp);
+ for (;;) {
+ int rd = json_read_binary_data(&state, buf, sizeof(buf));
+ if (rd == 0) break;
+ if (err == 0) {
+ /* TODO: word size, mode */
+ if (context_write_mem(ctx, addr, buf, rd) < 0) {
+ err = errno;
+#if ENABLE_ExtendedMemoryErrorReports
+ context_get_mem_error_info(&err_info);
+#endif
+ }
+ else {
+ addr += rd;
+ }
+ }
+ size += rd;
+ }
+ json_read_binary_end(&state);
+ if (read_stream(inp) != 0) exception(ERR_JSON_SYNTAX);
+ if (read_stream(inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
+
+ send_event_memory_changed(ctx, addr0, size);
+
+ write_stringz(out, "R");
+ write_stringz(out, token);
+ write_errno(out, err);
+ if (err == 0) {
+ write_stringz(out, "null");
+ }
+ else {
+ write_ranges(out, addr0, (int)(addr - addr0), BYTE_CANNOT_WRITE, &err_info);
+ }
+ write_stream(out, MARKER_EOM);
+ clear_trap(&trap);
+ }
+ else {
+ trace(LOG_ALWAYS, "Exception in message handler: %d %s",
+ trap.error, errno_to_str(trap.error));
+ channel_close(c);
+ }
+ }
+ channel_unlock(c);
+ context_unlock(ctx);
+ loc_free(args);
+}
+
+static void command_set(char * token, Channel * c) {
+ MemoryCommandArgs * args = read_command_args(token, c, CMD_SET);
+ if (args != NULL) post_safe_event(args->ctx, safe_memory_set, args);
+}
+
+static void safe_memory_get(void * parm) {
+ MemoryCommandArgs * args = (MemoryCommandArgs *)parm;
+ Channel * c = args->c;
+ Context * ctx = args->ctx;
+
+ if (!is_channel_closed(c)) {
+ Trap trap;
+ if (set_trap(&trap)) {
+ OutputStream * out = &args->c->out;
+ char * token = args->token;
+ ContextAddress addr0 = args->addr;
+ ContextAddress addr = args->addr;
+ unsigned long size = args->size;
+ unsigned long pos = 0;
+ char buf[BUF_SIZE];
+ int err = 0;
+ MemoryErrorInfo err_info;
+ JsonWriteBinaryState state;
+
+ if (ctx->exiting || ctx->exited) err = ERR_ALREADY_EXITED;
+
+ write_stringz(out, "R");
+ write_stringz(out, token);
+
+ json_write_binary_start(&state, out, size);
+ while (pos < size) {
+ int rd = size - pos;
+ if (rd > BUF_SIZE) rd = BUF_SIZE;
+ /* TODO: word size, mode */
+ memset(buf, 0, rd);
+ if (err == 0) {
+ if (context_read_mem(ctx, addr, buf, rd) < 0) {
+ err = errno;
+#if ENABLE_ExtendedMemoryErrorReports
+ context_get_mem_error_info(&err_info);
+#endif
+ }
+ else {
+ addr += rd;
+ }
+ }
+ json_write_binary_data(&state, buf, rd);
+ pos += rd;
+ }
+ json_write_binary_end(&state);
+ write_stream(out, 0);
+
+ write_errno(out, err);
+ if (err == 0) {
+ write_stringz(out, "null");
+ }
+ else {
+ write_ranges(out, addr0, (int)(addr - addr0), BYTE_CANNOT_READ, &err_info);
+ }
+ write_stream(out, MARKER_EOM);
+ clear_trap(&trap);
+ }
+ else {
+ trace(LOG_ALWAYS, "Exception in message handler: %d %s",
+ trap.error, errno_to_str(trap.error));
+ channel_close(c);
+ }
+ }
+ channel_unlock(c);
+ context_unlock(ctx);
+ loc_free(args);
+}
+
+static void command_get(char * token, Channel * c) {
+ MemoryCommandArgs * args = read_command_args(token, c, CMD_GET);
+ if (args != NULL) post_safe_event(args->ctx, safe_memory_get, args);
+}
+
+static void safe_memory_fill(void * parm) {
+ MemoryCommandArgs * args = (MemoryCommandArgs *)parm;
+ Channel * c = args->c;
+ Context * ctx = args->ctx;
+
+ if (!is_channel_closed(c)) {
+ Trap trap;
+ if (set_trap(&trap)) {
+ InputStream * inp = &c->inp;
+ OutputStream * out = &c->out;
+ char * token = args->token;
+ ContextAddress addr0 = args->addr;
+ ContextAddress addr = args->addr;
+ unsigned long size = args->size;
+ MemoryErrorInfo err_info;
+ char buf[0x1000];
+ int buf_pos = 0;
+ int err = 0;
+
+ if (ctx->exiting || ctx->exited) err = ERR_ALREADY_EXITED;
+
+ if (read_stream(inp) != '[') exception(ERR_JSON_SYNTAX);
+ if (peek_stream(inp) == ']') {
+ read_stream(inp);
+ }
+ else {
+ for (;;) {
+ int ch;
+ if (err == 0) {
+ if (buf_pos >= (int)sizeof(buf)) err = ERR_BUFFER_OVERFLOW;
+ else buf[buf_pos++] = (char)json_read_ulong(inp);
+ }
+ else {
+ json_read_ulong(inp);
+ }
+ ch = read_stream(inp);
+ if (ch == ',') continue;
+ if (ch == ']') break;
+ exception(ERR_JSON_SYNTAX);
+ }
+ }
+ if (read_stream(inp) != 0) exception(ERR_JSON_SYNTAX);
+ if (read_stream(inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
+
+ while (err == 0 && buf_pos < (int)size && buf_pos <= (int)(sizeof(buf) / 2)) {
+ if (buf_pos == 0) {
+ buf[buf_pos++] = 0;
+ }
+ else {
+ memcpy(buf + buf_pos, buf, buf_pos);
+ buf_pos *= 2;
+ }
+ }
+
+ while (err == 0 && addr < addr0 + size) {
+ char tmp[sizeof(buf)];
+ int wr = (int)(addr0 + size - addr);
+ if (wr > buf_pos) wr = buf_pos;
+ /* TODO: word size, mode */
+ memcpy(tmp, buf, wr);
+ if (context_write_mem(ctx, addr, tmp, wr) < 0) {
+ err = errno;
+#if ENABLE_ExtendedMemoryErrorReports
+ context_get_mem_error_info(&err_info);
+#endif
+ }
+ else {
+ addr += wr;
+ }
+ }
+
+ send_event_memory_changed(ctx, addr0, size);
+
+ write_stringz(out, "R");
+ write_stringz(out, token);
+ write_errno(out, err);
+ if (err == 0) {
+ write_stringz(out, "null");
+ }
+ else {
+ write_ranges(out, addr0, (int)(addr - addr0), BYTE_CANNOT_WRITE, &err_info);
+ }
+ write_stream(out, MARKER_EOM);
+ clear_trap(&trap);
+ }
+ else {
+ trace(LOG_ALWAYS, "Exception in message handler: %d %s",
+ trap.error, errno_to_str(trap.error));
+ channel_close(c);
+ }
+ }
+ channel_unlock(c);
+ context_unlock(ctx);
+ loc_free(args);
+}
+
+static void command_fill(char * token, Channel * c) {
+ MemoryCommandArgs * args = read_command_args(token, c, CMD_FILL);
+ if (args != NULL) post_safe_event(args->ctx, safe_memory_fill, args);
+}
+
+static void send_event_context_added(Context * ctx) {
+ OutputStream * out = &broadcast_group->out;
+
+ write_stringz(out, "E");
+ write_stringz(out, MEMORY);
+ write_stringz(out, "contextAdded");
+
+ /* <array of context data> */
+ write_stream(out, '[');
+ write_context(out, ctx);
+ write_stream(out, ']');
+ write_stream(out, 0);
+
+ write_stream(out, MARKER_EOM);
+}
+
+static void send_event_context_changed(Context * ctx) {
+ OutputStream * out = &broadcast_group->out;
+
+ write_stringz(out, "E");
+ write_stringz(out, MEMORY);
+ write_stringz(out, "contextChanged");
+
+ /* <array of context data> */
+ write_stream(out, '[');
+ write_context(out, ctx);
+ write_stream(out, ']');
+ write_stream(out, 0);
+
+ write_stream(out, MARKER_EOM);
+}
+
+static void send_event_context_removed(Context * ctx) {
+ OutputStream * out = &broadcast_group->out;
+
+ write_stringz(out, "E");
+ write_stringz(out, MEMORY);
+ write_stringz(out, "contextRemoved");
+
+ /* <array of context IDs> */
+ write_stream(out, '[');
+ json_write_string(out, ctx->id);
+ write_stream(out, ']');
+ write_stream(out, 0);
+
+ write_stream(out, MARKER_EOM);
+}
+
+static void event_context_created(Context * ctx, void * args) {
+ if (ctx->mem_access == 0) return;
+ send_event_context_added(ctx);
+}
+
+static void event_context_changed(Context * ctx, void * args) {
+ if (ctx->mem_access == 0) return;
+ send_event_context_changed(ctx);
+}
+
+static void event_context_exited(Context * ctx, void * args) {
+ if (ctx->mem_access == 0) return;
+ send_event_context_removed(ctx);
+}
+
+void ini_memory_service(Protocol * proto, TCFBroadcastGroup * bcg) {
+ static ContextEventListener listener = {
+ event_context_created,
+ event_context_exited,
+ NULL,
+ NULL,
+ event_context_changed
+ };
+ broadcast_group = bcg;
+ add_context_event_listener(&listener, NULL);
+ add_command_handler(proto, MEMORY, "getContext", command_get_context);
+ add_command_handler(proto, MEMORY, "getChildren", command_get_children);
+ add_command_handler(proto, MEMORY, "set", command_set);
+ add_command_handler(proto, MEMORY, "get", command_get);
+ add_command_handler(proto, MEMORY, "fill", command_fill);
+}
+
+#endif /* SERVICE_Memory */
diff --git a/agent/tcf/services/memoryservice.h b/agent/tcf/services/memoryservice.h
new file mode 100644
index 00000000..b560eeb3
--- /dev/null
+++ b/agent/tcf/services/memoryservice.h
@@ -0,0 +1,34 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+
+/*
+ * TCF Memory - memory access service.
+ */
+
+#ifndef D_memoryservice
+#define D_memoryservice
+
+/*
+ * Notify clients about memory content change.
+ */
+extern void send_event_memory_changed(Context * ctx, ContextAddress addr, unsigned long size);
+
+/*
+ * Initialize memory service.
+ */
+extern void ini_memory_service(Protocol * proto, TCFBroadcastGroup * bcg);
+
+
+#endif /* D_memoryservice */
diff --git a/agent/tcf/services/pathmap.c b/agent/tcf/services/pathmap.c
new file mode 100644
index 00000000..51f6e8b0
--- /dev/null
+++ b/agent/tcf/services/pathmap.c
@@ -0,0 +1,510 @@
+/*******************************************************************************
+ * Copyright (c) 2010, 2011 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
+ *******************************************************************************/
+
+/*
+ * Path Map service.
+ * The service manages file path mapping rules.
+ */
+
+#include <config.h>
+#include <assert.h>
+#include <framework/mdep-inet.h>
+#include <framework/myalloc.h>
+#include <services/pathmap.h>
+
+char * canonic_path_map_file_name(const char * fnm) {
+ static char * buf = NULL;
+ static size_t buf_pos = 0;
+ static size_t buf_max = 0;
+
+ buf_pos = 0;
+ for (;;) {
+ char ch = *fnm++;
+ if (ch == 0) break;
+ if (ch == '\\') ch = '/';
+ if (ch == '/' && buf_pos >= 2 && buf[buf_pos - 1] == '/') continue;
+ if (ch == '/' && *fnm == 0 && buf_pos > 0 && buf[buf_pos - 1] != ':') break;
+ if (ch == '.' && (buf_pos == 0 || buf[buf_pos - 1] == '/')) {
+ if (*fnm == '/' || *fnm == '\\') {
+ fnm++;
+ continue;
+ }
+ if (buf_pos > 0 && *fnm == '.' && (fnm[1] == '/' || fnm[1] == '\\')) {
+ unsigned j = buf_pos - 1;
+ if (j > 0 && buf[j - 1] != '/') {
+ while (j > 0 && buf[j - 1] != '/') j--;
+ buf_pos = j;
+ fnm += 2;
+ continue;
+ }
+ }
+ }
+ if (buf_pos == 0 && ch >= 'a' && ch <= 'z' && *fnm == ':') {
+ ch = ch - 'a' + 'A';
+ }
+ if (buf_pos + 1 >= buf_max) {
+ buf_max += 0x100;
+ buf = (char *)loc_realloc(buf, buf_max);
+ }
+ buf[buf_pos++] = ch;
+ }
+ buf[buf_pos] = 0;
+ return buf;
+}
+
+#if SERVICE_PathMap
+
+#include <stdio.h>
+#include <sys/stat.h>
+#include <framework/json.h>
+#include <framework/events.h>
+#include <framework/exceptions.h>
+
+typedef struct Listener Listener;
+typedef struct PathMap PathMap;
+
+struct Listener {
+ PathMapEventListener * listener;
+ void * args;
+};
+
+struct PathMapRule {
+ PathMapRuleAttribute * attrs;
+ char * src;
+ char * dst;
+ char * host;
+ char * prot;
+ char * ctx;
+};
+
+struct PathMap {
+ LINK maps;
+ Channel * channel;
+ PathMapRule * rules;
+ unsigned rules_cnt;
+ unsigned rules_max;
+};
+
+#define maps2map(x) ((PathMap *)((char *)(x) - offsetof(PathMap, maps)))
+
+static const char PATH_MAP[] = "PathMap";
+
+static int ini_done = 0;
+static LINK maps;
+static char host_name[256];
+
+static Listener * listeners = NULL;
+static unsigned listener_cnt = 0;
+static unsigned listener_max = 0;
+
+static void path_map_event_mapping_changed(Channel * c) {
+ unsigned i;
+ for (i = 0; i < listener_cnt; i++) {
+ Listener * l = listeners + i;
+ if (l->listener->mapping_changed == NULL) continue;
+ l->listener->mapping_changed(c, l->args);
+ }
+}
+
+void add_path_map_event_listener(PathMapEventListener * listener, void * args) {
+ 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 = args;
+}
+
+void rem_path_map_event_listener(PathMapEventListener * listener) {
+ unsigned i = 0;
+ while (i < listener_cnt) {
+ if (listeners[i++].listener == listener) {
+ while (i < listener_cnt) {
+ listeners[i - 1] = listeners[i];
+ i++;
+ }
+ listener_cnt--;
+ break;
+ }
+ }
+}
+
+static PathMap * find_map(Channel * c) {
+ LINK * l;
+ for (l = maps.next; l != &maps; l = l->next) {
+ PathMap * m = maps2map(l);
+ if (m->channel == c) return m;
+ }
+ return NULL;
+}
+
+static void flush_host_name(void * args) {
+ memset(host_name, 0, sizeof(host_name));
+}
+
+static int is_my_host(char * host) {
+ if (host == NULL || host[0] == 0) return 1;
+ if (host_name[0] == 0) {
+ gethostname(host_name, sizeof(host_name));
+ if (host_name[0] != 0) post_event_with_delay(flush_host_name, NULL, 1000000);
+ }
+ return strcasecmp(host, host_name) == 0;
+}
+
+static int update_rule(PathMapRule * r, PathMapRuleAttribute * new_attrs) {
+ int diff = 0;
+ PathMapRuleAttribute * old_attrs = r->attrs;
+ PathMapRuleAttribute ** new_ref = &r->attrs;
+ r->attrs = NULL;
+
+ while (new_attrs != NULL) {
+ PathMapRuleAttribute * new_attr = new_attrs;
+ PathMapRuleAttribute * old_attr = old_attrs;
+ PathMapRuleAttribute ** old_ref = &old_attrs;
+ InputStream * buf_inp = NULL;
+ ByteArrayInputStream buf;
+ char * name = new_attr->name;
+
+ new_attrs = new_attr->next;
+ new_attr->next = NULL;
+ while (old_attr && strcmp(old_attr->name, name)) {
+ old_ref = &old_attr->next;
+ old_attr = old_attr->next;
+ }
+
+ if (old_attr != NULL) {
+ assert(old_attr == *old_ref);
+ *old_ref = old_attr->next;
+ old_attr->next = NULL;
+ if (strcmp(old_attr->value, new_attr->value) == 0) {
+ *new_ref = old_attr;
+ new_ref = &old_attr->next;
+ loc_free(new_attr->value);
+ loc_free(new_attr->name);
+ loc_free(new_attr);
+ continue;
+ }
+ diff++;
+ loc_free(old_attr->value);
+ loc_free(old_attr->name);
+ loc_free(old_attr);
+ old_attr = NULL;
+ }
+
+ *new_ref = new_attr;
+ new_ref = &new_attr->next;
+
+ buf_inp = create_byte_array_input_stream(&buf, new_attr->value, strlen(new_attr->value));
+
+ if (strcmp(name, PATH_MAP_SOURCE) == 0) {
+ loc_free(r->src);
+ r->src = json_read_alloc_string(buf_inp);
+ }
+ else if (strcmp(name, PATH_MAP_DESTINATION) == 0) {
+ loc_free(r->dst);
+ r->dst = json_read_alloc_string(buf_inp);
+ }
+ else if (strcmp(name, PATH_MAP_PROTOCOL) == 0) {
+ loc_free(r->prot);
+ r->prot = json_read_alloc_string(buf_inp);
+ }
+ else if (strcmp(name, PATH_MAP_HOST) == 0) {
+ loc_free(r->host);
+ r->host = json_read_alloc_string(buf_inp);
+ }
+ else if (strcmp(name, PATH_MAP_CONTEXT) == 0) {
+ loc_free(r->ctx);
+ r->ctx = json_read_alloc_string(buf_inp);
+ }
+ }
+
+ while (old_attrs != NULL) {
+ PathMapRuleAttribute * old_attr = old_attrs;
+ char * name = old_attr->name;
+ old_attrs = old_attr->next;
+
+ if (strcmp(name, PATH_MAP_SOURCE) == 0) {
+ loc_free(r->src);
+ r->src = NULL;
+ }
+ else if (strcmp(name, PATH_MAP_DESTINATION) == 0) {
+ loc_free(r->dst);
+ r->dst = NULL;
+ }
+ else if (strcmp(name, PATH_MAP_PROTOCOL) == 0) {
+ loc_free(r->prot);
+ r->prot = NULL;
+ }
+ else if (strcmp(name, PATH_MAP_HOST) == 0) {
+ loc_free(r->host);
+ r->host = NULL;
+ }
+ else if (strcmp(name, PATH_MAP_CONTEXT) == 0) {
+ loc_free(r->ctx);
+ r->ctx = NULL;
+ }
+
+ loc_free(old_attr->value);
+ loc_free(old_attr->name);
+ loc_free(old_attr);
+ diff++;
+ }
+
+ return diff;
+}
+
+static char * map_file_name(Context * ctx, PathMap * m, char * fnm, int mode) {
+ unsigned i, j, k;
+ static char buf[FILE_PATH_SIZE];
+
+ for (i = 0; i < m->rules_cnt; i++) {
+ PathMapRule * r = m->rules + i;
+ char * src;
+ struct stat st;
+ if (r->src == NULL) continue;
+ if (r->dst == NULL) continue;
+ if (r->prot != NULL && strcasecmp(r->prot, "file")) continue;
+ switch (mode) {
+ case PATH_MAP_TO_LOCAL:
+ if (r->host != NULL && !is_my_host(r->host)) continue;
+ break;
+ }
+ if (r->ctx != NULL) {
+ int ok = 0;
+#if ENABLE_DebugContext
+ Context * syms = context_get_group(ctx, CONTEXT_GROUP_SYMBOLS);
+ if (syms != NULL) {
+ ok = strcmp(r->ctx, syms->id) == 0;
+ if (!ok && syms->name != NULL) {
+ ok = strcmp(r->ctx, syms->name) == 0;
+ if (!ok) ok = strcmp(r->ctx, context_full_name(syms)) == 0;
+ }
+ }
+#endif
+ if (!ok) continue;
+ }
+ src = canonic_path_map_file_name(r->src);
+ k = strlen(src);
+ if (strncmp(src, fnm, k)) continue;
+ if (fnm[k] != 0 && fnm[k] != '/' && fnm[k] != '\\') continue;
+ j = strlen(r->dst) - 1;
+ if (fnm[k] != 0 && (r->dst[j] == '/' || r->dst[j] == '\\')) k++;
+ snprintf(buf, sizeof(buf), "%s%s", r->dst, fnm + k);
+ if (mode != PATH_MAP_TO_LOCAL || stat(buf, &st) == 0) return buf;
+ }
+
+ return fnm;
+}
+
+char * apply_path_map(Channel * c, Context * ctx, char * fnm, int mode) {
+ if (c == NULL) {
+ LINK * l = maps.next;
+ while (l != &maps) {
+ PathMap * m = maps2map(l);
+ char * lnm = map_file_name(ctx, m, fnm, mode);
+ if (lnm != fnm) return lnm;
+ l = l->next;
+ }
+ }
+ else {
+ PathMap * m = find_map(c);
+ if (m == NULL) return NULL;
+ return map_file_name(ctx, m, fnm, mode);
+ }
+ return fnm;
+}
+
+void iterate_path_map_rules(Channel * channel, IteratePathMapsCallBack * callback, void * args) {
+ PathMap * m = find_map(channel);
+ if (m != NULL) {
+ unsigned i;
+ for (i = 0; i < m->rules_cnt; i++) {
+ callback(m->rules + i, args);
+ }
+ }
+}
+
+PathMapRuleAttribute * get_path_mapping_attributes(PathMapRule * map) {
+ return map->attrs;
+}
+
+PathMapRule * create_path_mapping(PathMapRuleAttribute * attrs) {
+ PathMapRule * r = NULL;
+ PathMap * m = find_map(NULL);
+
+ if (m == NULL) {
+ m = (PathMap *)loc_alloc_zero(sizeof(PathMap));
+ list_add_first(&m->maps, &maps);
+ }
+ if (m->rules_cnt >= m->rules_max) {
+ m->rules_max = m->rules_max ? m->rules_max * 2 : 8;
+ m->rules = (PathMapRule *)loc_realloc(m->rules, m->rules_max * sizeof(*m->rules));
+ }
+
+ r = m->rules + m->rules_cnt++;
+ memset(r, 0, sizeof(*r));
+ if (update_rule(r, attrs)) path_map_event_mapping_changed(NULL);
+ return r;
+}
+
+void change_path_mapping_attributes(PathMapRule * r, PathMapRuleAttribute * attrs) {
+ if (update_rule(r, attrs)) path_map_event_mapping_changed(NULL);
+}
+
+/*
+ * Delete a path mapping rule.
+ */
+extern void delete_path_mapping(PathMapRule * bp);
+
+static void write_rule(OutputStream * out, PathMapRule * r) {
+ unsigned i = 0;
+ PathMapRuleAttribute * attr = r->attrs;
+
+ write_stream(out, '{');
+ while (attr != NULL) {
+ if (i > 0) write_stream(out, ',');
+ json_write_string(out, attr->name);
+ write_stream(out, ':');
+ write_string(out, attr->value);
+ attr = attr->next;
+ i++;
+ }
+ write_stream(out, '}');
+}
+
+static void read_rule_attrs(InputStream * inp, const char * name, void * args) {
+ PathMapRuleAttribute *** list = (PathMapRuleAttribute ***)args;
+ PathMapRuleAttribute * attr = (PathMapRuleAttribute *)loc_alloc_zero(sizeof(PathMapRuleAttribute));
+
+ attr->name = loc_strdup(name);
+ attr->value = json_read_object(inp);
+ **list = attr;
+ *list = &attr->next;
+}
+
+static void read_rule(InputStream * inp, void * args) {
+ PathMap * m = (PathMap *)args;
+ PathMapRule * r = NULL;
+ PathMapRuleAttribute * attrs = NULL;
+ PathMapRuleAttribute ** attr_list = &attrs;
+
+
+ if (m->rules_cnt >= m->rules_max) {
+ m->rules_max = m->rules_max ? m->rules_max * 2 : 8;
+ m->rules = (PathMapRule *)loc_realloc(m->rules, m->rules_max * sizeof(*m->rules));
+ }
+
+ r = m->rules + m->rules_cnt;
+ memset(r, 0, sizeof(*r));
+ if (json_read_struct(inp, read_rule_attrs, &attr_list)) m->rules_cnt++;
+ update_rule(r, attrs);
+}
+
+static void free_rule(PathMapRule * r) {
+ loc_free(r->src);
+ loc_free(r->dst);
+ loc_free(r->host);
+ loc_free(r->prot);
+ loc_free(r->ctx);
+ while (r->attrs != NULL) {
+ PathMapRuleAttribute * attr = r->attrs;
+ r->attrs = attr->next;
+ loc_free(attr->name);
+ loc_free(attr->value);
+ loc_free(attr);
+ }
+ memset(r, 0, sizeof(PathMapRule));
+}
+
+void set_path_map(Channel * c, InputStream * inp) {
+ PathMap * m = find_map(c);
+
+ if (m == NULL) {
+ m = (PathMap *)loc_alloc_zero(sizeof(PathMap));
+ m->channel = c;
+ list_add_first(&m->maps, &maps);
+ }
+ else {
+ unsigned i;
+ for (i = 0; i < m->rules_cnt; i++) free_rule(m->rules + i);
+ m->rules_cnt = 0;
+ }
+ json_read_array(inp, read_rule, m);
+ path_map_event_mapping_changed(c);
+}
+
+static void command_get(char * token, Channel * c) {
+ PathMap * m = (PathMap *)find_map(c);
+
+ if (read_stream(&c->inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
+
+ write_stringz(&c->out, "R");
+ write_stringz(&c->out, token);
+ write_errno(&c->out, 0);
+ if (m == NULL) {
+ write_stringz(&c->out, "null");
+ }
+ else {
+ unsigned i;
+ write_stream(&c->out, '[');
+ for (i = 0; i < m->rules_cnt; i++) {
+ PathMapRule * r = m->rules + i;
+ if (i > 0) write_stream(&c->out, ',');
+ write_rule(&c->out, r);
+ }
+ write_stream(&c->out, ']');
+ write_stream(&c->out, 0);
+ }
+ write_stream(&c->out, MARKER_EOM);
+}
+
+static void command_set(char * token, Channel * c) {
+ set_path_map(c, &c->inp);
+
+ if (read_stream(&c->inp) != 0) exception(ERR_JSON_SYNTAX);
+ if (read_stream(&c->inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
+
+ 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) {
+ unsigned i;
+ PathMap * m = NULL;
+ /* Keep path map over channel redirection */
+ if (c->state == ChannelStateHelloReceived) return;
+ m = find_map(c);
+ if (m == NULL) return;
+ list_remove(&m->maps);
+ for (i = 0; i < m->rules_cnt; i++) free_rule(m->rules + i);
+ loc_free(m->rules);
+ loc_free(m);
+}
+
+void ini_path_map_service(Protocol * proto) {
+ if (!ini_done) {
+ ini_done = 1;
+ list_init(&maps);
+ add_channel_close_listener(channel_close_listener);
+ }
+ add_command_handler(proto, PATH_MAP, "get", command_get);
+ add_command_handler(proto, PATH_MAP, "set", command_set);
+}
+
+#endif /* SERVICE_PathMap */
diff --git a/agent/tcf/services/pathmap.h b/agent/tcf/services/pathmap.h
new file mode 100644
index 00000000..3e78b0b2
--- /dev/null
+++ b/agent/tcf/services/pathmap.h
@@ -0,0 +1,125 @@
+/*******************************************************************************
+ * Copyright (c) 2010, 2011 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
+ *******************************************************************************/
+
+/*
+ * Path Map service.
+ * The service manages file path mapping rules.
+ */
+
+#ifndef D_pathmap
+#define D_pathmap
+
+#include <config.h>
+#include <framework/context.h>
+#include <framework/protocol.h>
+
+/*
+ * Convert a file name to canonic form that is suitable for file name comparisons.
+ * Unlike canonicalize_file_name() or realpath(), the function can be used for remote files.
+ * Return pointer to a static array with converted name.
+ */
+extern char * canonic_path_map_file_name(const char * fnm);
+
+#if SERVICE_PathMap
+
+typedef struct PathMapRule PathMapRule;
+typedef struct PathMapRuleAttribute PathMapRuleAttribute;
+
+struct PathMapRuleAttribute {
+ PathMapRuleAttribute * next;
+ char * name; /* Attribute name */
+ char * value; /* Attribute value as JSON string */
+};
+
+/* Path map attribute names */
+#define PATH_MAP_ID "ID"
+#define PATH_MAP_SOURCE "Source"
+#define PATH_MAP_DESTINATION "Destination"
+#define PATH_MAP_CONTEXT "Context"
+#define PATH_MAP_HOST "Host"
+#define PATH_MAP_PROTOCOL "Protocol"
+
+/*
+ * Path map events listener.
+ */
+typedef struct PathMapEventListener {
+ void (*mapping_changed)(Channel * c, void * args);
+} PathMapEventListener;
+
+/*
+ * Add path map events listener.
+ */
+extern void add_path_map_event_listener(PathMapEventListener * listener, void * args);
+
+/*
+ * Remove path map events listener.
+ */
+extern void rem_path_map_event_listener(PathMapEventListener * listener);
+
+/*
+ * Iterate all path mapping rules registerd for given client,
+ * channel == NULL iterates rules that were created by create_path_mapping() function.
+ */
+typedef void IteratePathMapsCallBack(PathMapRule *, void *);
+extern void iterate_path_map_rules(Channel * channel, IteratePathMapsCallBack * callback, void * args);
+
+/*
+ * Get path mapping rule attributes.
+ */
+extern PathMapRuleAttribute * get_path_mapping_attributes(PathMapRule * map);
+
+/*
+ * Create new path mapping rule with given attributes.
+ * Caller should allocate attributes using myalloc.h functions.
+ * Path Map service will free attributes memory using loc_free().
+ */
+extern PathMapRule * create_path_mapping(PathMapRuleAttribute * attrs);
+
+/*
+ * Change path mapping rule attributes to given attributes.
+ * Caller should allocate attributes using myalloc.h functions.
+ * Path Map service will free attributes memory using loc_free().
+ * The function compares existing attributes with new ones,
+ * and calls listeners only if attributes are different.
+ */
+extern void change_path_mapping_attributes(PathMapRule * map, PathMapRuleAttribute * attrs);
+
+/*
+ * Delete a path mapping rule.
+ */
+extern void delete_path_mapping(PathMapRule * bp);
+
+#define PATH_MAP_TO_CLIENT 1
+#define PATH_MAP_TO_LOCAL 2
+#define PATH_MAP_TO_TARGET 3
+
+/*
+ * Translate debug file name to local or target file name using file path mapping table of given channel.
+ * Return pointer to static buffer that contains translated file name.
+ */
+extern char * apply_path_map(Channel * channel, Context * ctx, char * file_name, int mode);
+
+/*
+ * Read new path map from the given input stream.
+ * The function is used by forwarding value-add server,
+ * and it is not intended for use by other clients.
+ */
+extern void set_path_map(Channel * channel, InputStream * inp);
+
+extern void ini_path_map_service(Protocol * proto);
+
+#endif /* SERVICE_PathMap */
+
+#endif /* D_pathmap */
diff --git a/agent/tcf/services/processes.c b/agent/tcf/services/processes.c
new file mode 100644
index 00000000..78a2a565
--- /dev/null
+++ b/agent/tcf/services/processes.c
@@ -0,0 +1,1495 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2011 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 Processes - process control service.
+ * Processes service provides access to the target OS's process information,
+ * allows to start and terminate a process, and allows to attach and
+ * detach a process for debugging. Debug services, like Memory and Run Control,
+ * require a process to be attached before they can access it.
+ */
+
+/* TODO: It should be possible to filter processes on a criteria (user, name, etc) */
+
+#if defined(__GNUC__) && !defined(_GNU_SOURCE)
+# define _GNU_SOURCE
+#endif
+
+#include <config.h>
+
+#if SERVICE_Processes || SERVICE_Terminals
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.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/asyncreq.h>
+#include <framework/exceptions.h>
+#include <framework/waitpid.h>
+#include <framework/signames.h>
+#include <services/streamsservice.h>
+#include <services/processes.h>
+
+#if SERVICE_Processes
+static const char * PROCESSES[2] = { "Processes", "ProcessesV1" };
+#endif
+
+#if defined(WIN32)
+# include <tlhelp32.h>
+# ifdef _MSC_VER
+# pragma warning(disable:4201) /* nonstandard extension used : nameless struct/union (in winternl.h) */
+# include <winternl.h>
+# else
+# include <ntdef.h>
+# endif
+# ifndef STATUS_INFO_LENGTH_MISMATCH
+# define STATUS_INFO_LENGTH_MISMATCH ((NTSTATUS)0xC0000004L)
+# endif
+# ifndef SystemHandleInformation
+# define SystemHandleInformation 16
+# endif
+#elif defined(_WRS_KERNEL)
+# include <symLib.h>
+# include <sysSymTbl.h>
+# include <ioLib.h>
+# include <ptyDrv.h>
+# include <taskHookLib.h>
+#else
+# include <sys/stat.h>
+# include <unistd.h>
+# include <dirent.h>
+#endif
+
+#define PBUF_SIZE 0x400
+#define PIPE_SIZE 0x400
+#define SBUF_SIZE 0x1000
+
+typedef struct AttachDoneArgs {
+ Channel * c;
+ char token[256];
+} AttachDoneArgs;
+
+struct ChildProcess {
+ LINK link;
+ int pid;
+ int tty;
+ int got_output;
+ TCFBroadcastGroup * bcg;
+ struct ProcessInput * inp_struct;
+ struct ProcessOutput * out_struct;
+ struct ProcessOutput * err_struct;
+ char name[256];
+ char service[256];
+ long exit_code;
+ EventCallBack * exit_cb;
+ void * exit_args;
+};
+
+typedef struct ProcessOutput {
+ int fd;
+ char id[256];
+ ChildProcess * prs;
+ AsyncReqInfo req;
+ int req_posted;
+ char buf[PBUF_SIZE];
+ size_t buf_pos;
+ int eos;
+ VirtualStream * vstream;
+} ProcessOutput;
+
+typedef struct ProcessInput {
+ int fd;
+ char id[256];
+ ChildProcess * prs;
+ AsyncReqInfo req;
+ int req_posted;
+ char buf[PBUF_SIZE];
+ size_t buf_pos;
+ size_t buf_len;
+ int eos;
+ VirtualStream * vstream;
+} ProcessInput;
+
+#define link2prs(A) ((ChildProcess *)((char *)(A) - offsetof(ChildProcess, link)))
+
+static int init_done = 0;
+static LINK prs_list;
+#if defined(_WRS_KERNEL)
+static SEM_ID prs_list_lock = NULL;
+#endif
+
+static ChildProcess * find_process(int pid) {
+ LINK * qhp = &prs_list;
+ LINK * qp = qhp->next;
+
+ while (qp != qhp) {
+ ChildProcess * prs = link2prs(qp);
+ if (prs->pid == pid) return prs;
+ qp = qp->next;
+ }
+ return NULL;
+}
+
+#if SERVICE_Processes
+
+static int is_attached(pid_t pid) {
+#if ENABLE_DebugContext
+ return context_find_from_pid(pid, 0) != NULL;
+#else
+ return 0;
+#endif
+}
+
+static void write_context(OutputStream * out, int pid) {
+ ChildProcess * prs = find_process(pid);
+
+ write_stream(out, '{');
+
+ /* the process Name */
+#if defined(__linux__)
+ /* Use the /proc to get the name */
+ {
+ char buff[256];
+ char fname[256];
+ FILE * file;
+ const char * name = NULL;
+
+ /* clear out buff */
+ buff[0] = 0;
+
+ /* try cmdline */
+ snprintf(fname, sizeof(fname), "/proc/%d/cmdline", pid);
+ file = fopen(fname, "r");
+ if (file) {
+ if (fgets(buff, sizeof(buff), file) != NULL) {
+ if (buff[0]) name = buff;
+ }
+ fclose(file);
+ }
+
+ if (!name) {
+ /* try status */
+ snprintf(fname, sizeof(fname), "/proc/%d/status", pid);
+ file = fopen(fname, "r");
+ if (file) {
+ char * p = fgets(buff, sizeof(buff), file);
+ if (p != NULL) {
+ /* Find the attribute name */
+ for (; *p; ++p) {
+ if (*p == ':') {
+ /* close off the attr name string */
+ *p++ = 0;
+
+ /* is it our name? */
+ if (!strcmp(buff, "Name")) {
+ char * n;
+
+ /* change tab to '[' */
+ *p = '[';
+
+ /* change trailing new line to ']' */
+ for (n = p; *n; ++n)
+ if (*n == '\n')
+ *n = ']';
+
+ name = p;
+ break;
+ }
+ }
+ }
+ }
+ fclose(file);
+ }
+ }
+
+ if (!name) name = pid2id(pid, 0);
+
+ /* Send it out */
+ json_write_string(out, "Name");
+ write_stream(out, ':');
+ json_write_string(out, name);
+ write_stream(out, ',');
+ }
+#else
+ json_write_string(out, "Name");
+ write_stream(out, ':');
+ json_write_string(out, prs ? prs->name : pid2id(pid, 0));
+ write_stream(out, ',');
+#endif
+
+ json_write_string(out, "CanTerminate");
+ write_stream(out, ':');
+ json_write_boolean(out, 1);
+ write_stream(out, ',');
+
+ if (is_attached(pid)) {
+ json_write_string(out, "Attached");
+ write_stream(out, ':');
+ json_write_boolean(out, 1);
+ write_stream(out, ',');
+ }
+
+ if (prs != NULL) {
+ if (prs->inp_struct) {
+ json_write_string(out, "StdInID");
+ write_stream(out, ':');
+ json_write_string(out, prs->inp_struct->id);
+ write_stream(out, ',');
+ }
+ if (prs->out_struct) {
+ json_write_string(out, "StdOutID");
+ write_stream(out, ':');
+ json_write_string(out, prs->out_struct->id);
+ write_stream(out, ',');
+ }
+ if (prs->err_struct) {
+ json_write_string(out, "StdErrID");
+ write_stream(out, ':');
+ json_write_string(out, prs->err_struct->id);
+ write_stream(out, ',');
+ }
+ }
+
+ json_write_string(out, "ID");
+ write_stream(out, ':');
+ json_write_string(out, pid2id(pid, 0));
+
+ write_stream(out, '}');
+}
+
+static void send_event_process_exited(OutputStream * out, ChildProcess * prs) {
+ int v;
+ for (v = 0; v < 2; v++) {
+ write_stringz(out, "E");
+ write_stringz(out, PROCESSES[v]);
+ write_stringz(out, "exited");
+
+ json_write_string(out, pid2id(prs->pid, 0));
+ write_stream(out, 0);
+
+ json_write_long(out, prs->exit_code);
+ write_stream(out, 0);
+
+ write_stream(out, MARKER_EOM);
+ }
+}
+
+static void command_get_context(char * token, Channel * c) {
+ int err = 0;
+ char id[256];
+ pid_t pid, parent;
+
+ 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);
+
+ pid = id2pid(id, &parent);
+ write_stringz(&c->out, "R");
+ write_stringz(&c->out, token);
+
+ if (pid != 0 && parent == 0) {
+#if defined(WIN32)
+#elif defined(_WRS_KERNEL)
+ if (TASK_ID_VERIFY(pid) == ERROR) err = ERR_INV_CONTEXT;
+#elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__APPLE__)
+#else
+ struct stat st;
+ char dir[FILE_PATH_SIZE];
+ snprintf(dir, sizeof(dir), "/proc/%d", pid);
+ if (lstat(dir, &st) < 0) err = errno;
+ else if (!S_ISDIR(st.st_mode)) err = ERR_INV_CONTEXT;
+#endif
+ }
+
+ write_errno(&c->out, err);
+
+ if (err == 0 && pid != 0 && parent == 0) {
+ write_context(&c->out, pid);
+ write_stream(&c->out, 0);
+ }
+ else {
+ write_stringz(&c->out, "null");
+ }
+
+ write_stream(&c->out, MARKER_EOM);
+}
+
+static void command_get_children(char * token, Channel * c) {
+ char id[256];
+ int attached_only;
+
+ json_read_string(&c->inp, id, sizeof(id));
+ if (read_stream(&c->inp) != 0) exception(ERR_JSON_SYNTAX);
+ attached_only = json_read_boolean(&c->inp);
+ if (read_stream(&c->inp) != 0) exception(ERR_JSON_SYNTAX);
+ if (read_stream(&c->inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
+
+ write_stringz(&c->out, "R");
+ write_stringz(&c->out, token);
+
+ if (id[0] != 0) {
+ write_errno(&c->out, 0);
+ write_stringz(&c->out, "null");
+ }
+ else {
+#if defined(WIN32)
+ DWORD err = 0;
+ HANDLE snapshot;
+ PROCESSENTRY32 pe32;
+
+ snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
+ if (snapshot == INVALID_HANDLE_VALUE) err = set_win32_errno(GetLastError());
+ memset(&pe32, 0, sizeof(pe32));
+ pe32.dwSize = sizeof(PROCESSENTRY32);
+ if (!err && !Process32First(snapshot, &pe32)) {
+ err = set_win32_errno(GetLastError());
+ CloseHandle(snapshot);
+ }
+ write_errno(&c->out, err);
+ if (err) {
+ write_stringz(&c->out, "null");
+ }
+ else {
+ int cnt = 0;
+ write_stream(&c->out, '[');
+ do {
+ if (!attached_only || is_attached(pe32.th32ProcessID)) {
+ if (cnt > 0) write_stream(&c->out, ',');
+ json_write_string(&c->out, pid2id(pe32.th32ProcessID, 0));
+ cnt++;
+ }
+ }
+ while (Process32Next(snapshot, &pe32));
+ write_stream(&c->out, ']');
+ write_stream(&c->out, 0);
+ }
+ if (snapshot != INVALID_HANDLE_VALUE) CloseHandle(snapshot);
+#elif defined(_WRS_KERNEL)
+ int i = 0;
+ int cnt = 0;
+ int ids_cnt = 0;
+ int ids_max = 500;
+ int * ids = (int *)loc_alloc(ids_max * sizeof(int));
+ for (;;) {
+ ids_cnt = taskIdListGet(ids, ids_max);
+ if (ids_cnt < ids_max) break;
+ loc_free(ids);
+ ids_max *= 2;
+ ids = (int *)loc_alloc(ids_max * sizeof(int));
+ }
+ write_errno(&c->out, 0);
+ write_stream(&c->out, '[');
+ for (i = 0; i < ids_cnt; i++) {
+ if (!attached_only || is_attached(ids[i])) {
+ if (cnt > 0) write_stream(&c->out, ',');
+ json_write_string(&c->out, pid2id(ids[i], 0));
+ cnt++;
+ }
+ }
+ write_stream(&c->out, ']');
+ write_stream(&c->out, 0);
+#elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__APPLE__)
+#else
+ DIR * proc = opendir("/proc");
+ if (proc == NULL) {
+ write_errno(&c->out, errno);
+ write_stringz(&c->out, "null");
+ }
+ else {
+ int cnt = 0;
+ write_errno(&c->out, 0);
+ write_stream(&c->out, '[');
+ for (;;) {
+ struct dirent * ent = readdir(proc);
+ if (ent == NULL) break;
+ if (ent->d_name[0] >= '1' && ent->d_name[0] <= '9') {
+ pid_t pid = atol(ent->d_name);
+ if (!attached_only || is_attached(pid)) {
+ if (cnt > 0) write_stream(&c->out, ',');
+ json_write_string(&c->out, pid2id(pid, 0));
+ cnt++;
+ }
+ }
+ }
+ write_stream(&c->out, ']');
+ write_stream(&c->out, 0);
+ closedir(proc);
+ }
+#endif
+ }
+
+ write_stream(&c->out, MARKER_EOM);
+}
+
+#if ENABLE_DebugContext
+
+static void attach_done(int error, Context * ctx, void * arg) {
+ AttachDoneArgs * data = (AttachDoneArgs *)arg;
+ Channel * c = data->c;
+
+ if (!is_channel_closed(c)) {
+ write_stringz(&c->out, "R");
+ write_stringz(&c->out, data->token);
+ write_errno(&c->out, error);
+ write_stream(&c->out, MARKER_EOM);
+ }
+ channel_unlock(c);
+ loc_free(data);
+}
+
+static void command_attach(char * token, Channel * c) {
+ int err = 0;
+ char id[256];
+ pid_t pid, parent;
+
+ 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);
+
+ pid = id2pid(id, &parent);
+
+ if (parent != 0) {
+ err = ERR_INV_CONTEXT;
+ }
+ else if (is_attached(pid)) {
+ err = ERR_ALREADY_ATTACHED;