blob: 6d81c91b30cd3f45bf8608933eeb32ecbbcba050 [file] [log] [blame]
/*******************************************************************************
* 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.
*
* 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 SERVICE_Streams
if (buf_size0 <= 0 || buf_size1 <= 0) err = ERR_INV_NUMBER;
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
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
}