/******************************************************************************* * 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 defines agent error codes in addition to system codes defined in errno.h */ #include #include #include #include #include #include #include #include #include #include #include #include #define ERR_MESSAGE_MIN (STD_ERR_BASE + 100) #if MEM_USAGE_FACTOR <= 2 #define ERR_MESSAGE_MAX (STD_ERR_BASE + 129) #else #define ERR_MESSAGE_MAX (STD_ERR_BASE + 199) #endif #define MESSAGE_CNT (ERR_MESSAGE_MAX - ERR_MESSAGE_MIN + 1) #define SRC_SYSTEM 1 #define SRC_GAI 2 #define SRC_MESSAGE 3 #define SRC_REPORT 4 typedef struct ReportBuffer { ErrorReport pub; /* public part of error report */ int refs; int gets; } ReportBuffer; typedef struct ErrorMessage { int source; int error; char * text; ReportBuffer * report; } ErrorMessage; static ErrorMessage msgs[MESSAGE_CNT]; static int msgs_pos = 0; static char * msg_buf = NULL; static size_t msg_max = 0; static size_t msg_len = 0; static void realloc_msg_buf(void) { assert(is_dispatch_thread()); if (msg_max <= msg_len + 128 || msg_max > msg_len + 2048) { msg_max = msg_len + 256; msg_buf = (char *)loc_realloc(msg_buf, msg_max); } } static void release_report(ReportBuffer * report) { if (report == NULL) return; assert(report->refs > report->gets); report->refs--; if (report->refs == 0) { while (report->pub.props != NULL) { ErrorReportItem * i = report->pub.props; report->pub.props = i->next; loc_free(i->name); loc_free(i->value); loc_free(i); } while (report->pub.param_cnt > 0) { loc_free(report->pub.params[--report->pub.param_cnt]); } loc_free(report->pub.params); loc_free(report->pub.format); loc_free(report); } } static ErrorMessage * alloc_msg(int source) { ErrorMessage * m = msgs + msgs_pos; assert(is_dispatch_thread()); errno = ERR_MESSAGE_MIN + msgs_pos++; if (msgs_pos >= MESSAGE_CNT) msgs_pos = 0; release_report(m->report); loc_free(m->text); m->source = source; m->error = 0; m->report = NULL; m->text = NULL; return m; } #ifdef WIN32 static char * system_strerror(DWORD errno_win32) { WCHAR * buf = NULL; assert(is_dispatch_thread()); msg_len = 0; if (FormatMessageW( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_MAX_WIDTH_MASK, NULL, errno_win32, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), /* Default language */ (LPWSTR)&buf, 0, NULL)) { msg_len = WideCharToMultiByte(CP_UTF8, 0, buf, -1, NULL, 0, NULL, NULL); if (msg_len > 0) { realloc_msg_buf(); msg_len = WideCharToMultiByte(CP_UTF8, 0, buf, -1, msg_buf, msg_max, NULL, NULL); } } if (msg_len == 0) { realloc_msg_buf(); msg_len = snprintf(msg_buf, msg_max, "System error code 0x%lx", (unsigned long)errno_win32); } if (buf != NULL) LocalFree(buf); while (msg_len > 0 && msg_buf[msg_len - 1] <= ' ') msg_len--; if (msg_len > 0 && msg_buf[msg_len - 1] == '.') msg_len--; msg_buf[msg_len] = 0; return msg_buf; } typedef struct EventArgs { HANDLE done; int win32_code; int error_code; } EventArgs; static void set_win32_errno_event(void * args) { ErrorMessage * m = NULL; EventArgs * e = (EventArgs *)args; m = alloc_msg(SRC_SYSTEM); m->error = e->win32_code; e->error_code = errno; SetEvent(e->done); } int set_win32_errno(DWORD win32_error_code) { if (win32_error_code) { if (is_dispatch_thread()) { ErrorMessage * m = alloc_msg(SRC_SYSTEM); m->error = win32_error_code; } else { /* Called on background thread */ int error = 0; EventArgs * e = (EventArgs *)loc_alloc_zero(sizeof(EventArgs)); e->done = CreateEvent(NULL, TRUE, FALSE, NULL); e->win32_code = win32_error_code; post_event(set_win32_errno_event, e); WaitForSingleObject(e->done, INFINITE); CloseHandle(e->done); error = e->error_code; loc_free(e); errno = error; } } else { errno = 0; } return errno; } #elif defined(__SYMBIAN32__) #include static char * system_strerror(int err) { static char static_error[32]; switch (err) { case KErrNotFound: return "item not found"; case KErrNotSupported: return "functionality is not supported"; case KErrBadHandle: return "an invalid handle"; case KErrAccessDenied: return "access to a file is denied"; case KErrAlreadyExists: return "an object already exists"; case KErrWrite: return "error in write operation"; case KErrPermissionDenied: return "permission denied"; case KErrBadDescriptor: return "bad descriptor"; default: snprintf(static_error, sizeof(static_error), "Error code %d", err); return static_error; } } #endif static void append_format_parameter(char * type, char * style, char * param) { /* Note: 'param' is UTF-8 encoded JSON text */ char str[64]; if (param != NULL && (*param == '"' || strcmp(type, "number") == 0)) { Trap trap; ByteArrayInputStream buf; InputStream * inp = create_byte_array_input_stream(&buf, param, strlen(param)); if (set_trap(&trap)) { if (*param == '"') { char * x = json_read_alloc_string(inp); if (x != NULL) { char * s = x; while (*s) { realloc_msg_buf(); msg_buf[msg_len++] = *s++; } loc_free(x); } param = NULL; } else { double x = json_read_double(inp); if (strcmp(style, "percent") == 0) { snprintf(str, sizeof(str), "%ld%%", (long)(x * 100)); } else if (strcmp(style, "integer") == 0) { snprintf(str, sizeof(str), "%ld", (long)x); } else { snprintf(str, sizeof(str), "%g", x); } param = str; } clear_trap(&trap); } } if (param != NULL) { while (*param) { realloc_msg_buf(); msg_buf[msg_len++] = *param++; } } } static const char * format_error_report_message(const char * fmt, char ** params, int param_cnt) { int fmt_pos = 0; int in_quotes = 0; msg_len = 0; while (fmt[fmt_pos]) { char ch = fmt[fmt_pos++]; realloc_msg_buf(); if (in_quotes && ch == '\'') { in_quotes = 0; } else if (in_quotes) { msg_buf[msg_len++] = ch; } else if (ch == '\'' && fmt[fmt_pos] == '\'') { msg_buf[msg_len++] = ch; fmt_pos++; } else if (ch =='\'') { in_quotes = 1; } else if (ch == '{') { size_t j = 0; int index = 0; char type[16]; char style[16]; type[0] = style[0] = 0; while (fmt[fmt_pos] >= '0' && fmt[fmt_pos] <= '9') { index = index * 10 + (fmt[fmt_pos++] - '0'); } if (fmt[fmt_pos] == ',') { fmt_pos++; j = 0; while (fmt[fmt_pos] >= 'a' && fmt[fmt_pos] <= 'z') { ch = fmt[fmt_pos++]; if (j < sizeof(type) - 1) type[j++] = ch; } type[j++] = 0; if (fmt[fmt_pos] == ',') { fmt_pos++; j = 0; while (fmt[fmt_pos] >= 'a' && fmt[fmt_pos] <= 'z') { ch = fmt[fmt_pos++]; if (j < sizeof(style) - 1) style[j++] = ch; } style[j++] = 0; } } if (index < param_cnt) append_format_parameter(type, style, params[index]); while (fmt[fmt_pos] && fmt[fmt_pos] != '}') fmt_pos++; if (fmt[fmt_pos] == '}') fmt_pos++; } else { msg_buf[msg_len++] = ch; } } realloc_msg_buf(); msg_buf[msg_len++] = 0; return msg_buf; } const char * errno_to_str(int err) { switch (err) { case ERR_ALREADY_STOPPED: return "Already stopped"; case ERR_ALREADY_EXITED: return "Already exited"; case ERR_ALREADY_RUNNING: return "Already running"; case ERR_JSON_SYNTAX: return "JSON syntax error"; case ERR_PROTOCOL: return "Protocol format error"; case ERR_INV_CONTEXT: return "Invalid context"; case ERR_INV_ADDRESS: return "Invalid address"; case ERR_EOF: return "End of file"; case ERR_BASE64: return "Invalid BASE64 string"; case ERR_INV_EXPRESSION: return "Invalid expression"; case ERR_SYM_NOT_FOUND: return "Symbol not found"; case ERR_ALREADY_ATTACHED: return "Already attached"; case ERR_BUFFER_OVERFLOW: return "Buffer overflow"; case ERR_INV_FORMAT: return "Format is not supported"; case ERR_INV_NUMBER: return "Invalid number"; case ERR_IS_RUNNING: return "Execution context is running"; case ERR_INV_DWARF: return "Error reading DWARF data"; case ERR_UNSUPPORTED: return "Unsupported command"; case ERR_CHANNEL_CLOSED: return "Channel closed"; case ERR_COMMAND_CANCELLED: return "Command canceled"; case ERR_UNKNOWN_PEER: return "Unknown peer"; case ERR_INV_DATA_SIZE: return "Invalid data size"; case ERR_INV_DATA_TYPE: return "Invalid data type"; case ERR_INV_COMMAND: return "Command is not recognized"; case ERR_INV_TRANSPORT: return "Invalid transport name"; case ERR_CACHE_MISS: return "Invalid data cache state"; case ERR_NOT_ACTIVE: return "Context is not active"; default: if (err >= ERR_MESSAGE_MIN && err <= ERR_MESSAGE_MAX) { if (is_dispatch_thread()) { ErrorMessage * m = msgs + (err - ERR_MESSAGE_MIN); if (m->report != NULL && m->report->pub.format != NULL) { return format_error_report_message(m->report->pub.format, m->report->pub.params, m->report->pub.param_cnt); } switch (m->source) { #ifdef WIN32 case SRC_SYSTEM: return system_strerror(m->error); #endif case SRC_GAI: return loc_gai_strerror(m->error); case SRC_MESSAGE: return m->text; case SRC_REPORT: return errno_to_str(m->error); } } else { return "cannot get error message text: errno_to_str() must be called from the main thread"; } } #ifdef __SYMBIAN32__ if (err < 0) { return system_strerror(err); } #endif return strerror(err); } } int set_errno(int no, const char * msg) { errno = no; if (no != 0 && msg != NULL) { ErrorMessage * m = alloc_msg(SRC_MESSAGE); /* alloc_msg() assigns new value to 'errno', * need to be sure it does not change until this function exits. */ int err = errno; m->error = get_error_code(no); if (no == ERR_OTHER) { m->text = loc_strdup(msg); } else { const char * text0 = errno_to_str(no); size_t len = strlen(msg) + strlen(text0) + 4; char * text1 = (char *)loc_alloc(len); snprintf(text1, len, "%s. %s", msg, text0); m->text = text1; } errno = err; } return errno; } int set_gai_errno(int no) { errno = no; if (no != 0) { ErrorMessage * m = alloc_msg(SRC_GAI); m->error = no; } return errno; } int set_error_report_errno(ErrorReport * r) { errno = 0; if (r != NULL) { ReportBuffer * report = (ReportBuffer *)((char *)r - offsetof(ReportBuffer, pub)); ErrorMessage * m = alloc_msg(SRC_REPORT); m->error = report->pub.code + STD_ERR_BASE; m->report = report; report->refs++; } return errno; } int get_error_code(int no) { while (no >= ERR_MESSAGE_MIN && no <= ERR_MESSAGE_MAX) { ErrorMessage * m = msgs + (no - ERR_MESSAGE_MIN); assert(is_dispatch_thread()); switch (m->source) { case SRC_REPORT: case SRC_MESSAGE: no = m->error; continue; } return ERR_OTHER; } return no; } static void add_report_prop(ReportBuffer * report, const char * name, ByteArrayOutputStream * buf) { ErrorReportItem * i = (ErrorReportItem *)loc_alloc(sizeof(ErrorReportItem)); i->name = loc_strdup(name); get_byte_array_output_stream_data(buf, &i->value, NULL); i->next = report->pub.props; report->pub.props = i; } static void add_report_prop_int(ReportBuffer * report, const char * name, unsigned long n) { ByteArrayOutputStream buf; OutputStream * out = create_byte_array_output_stream(&buf); json_write_ulong(out, n); write_stream(out, 0); add_report_prop(report, name, &buf); } static void add_report_prop_str(ReportBuffer * report, const char * name, const char * str) { ByteArrayOutputStream buf; OutputStream * out = create_byte_array_output_stream(&buf); json_write_string(out, str); write_stream(out, 0); add_report_prop(report, name, &buf); } ErrorReport * get_error_report(int err) { ErrorMessage * m = NULL; if (err >= ERR_MESSAGE_MIN && err <= ERR_MESSAGE_MAX) { assert(is_dispatch_thread()); m = msgs + (err - ERR_MESSAGE_MIN); if (m->report != NULL) { m->report->refs++; m->report->gets++; return &m->report->pub; } } if (err != 0) { ReportBuffer * report = (ReportBuffer *)loc_alloc_zero(sizeof(ReportBuffer)); struct timespec timenow; if (clock_gettime(CLOCK_REALTIME, &timenow) == 0) { report->pub.time_stamp = (uint64_t)timenow.tv_sec * 1000 + timenow.tv_nsec / 1000000; } report->pub.format = loc_strdup(errno_to_str(err)); if (m != NULL) { if (m->source == SRC_MESSAGE) { err = m->error; } #ifdef WIN32 else if (m->source == SRC_SYSTEM) { add_report_prop_int(report, "AltCode", m->error); add_report_prop_str(report, "AltOrg", "WIN32"); err = ERR_OTHER; } #endif else { err = ERR_OTHER; } } if (err < STD_ERR_BASE || err > ERR_MESSAGE_MAX) { add_report_prop_int(report, "AltCode", err); #if defined(_MSC_VER) add_report_prop_str(report, "AltOrg", "MSC"); #elif defined(_WRS_KERNEL) add_report_prop_str(report, "AltOrg", "VxWorks"); #elif defined(__CYGWIN__) add_report_prop_str(report, "AltOrg", "CygWin"); #elif defined(__linux__) add_report_prop_str(report, "AltOrg", "Linux"); #elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__APPLE__) add_report_prop_str(report, "AltOrg", "BSD"); #elif defined(__SYMBIAN32__) add_report_prop_str(report, "AltOrg", "Symbian"); #else add_report_prop_str(report, "AltOrg", "POSIX"); #endif err = ERR_OTHER; } assert(err >= STD_ERR_BASE); assert(err < ERR_MESSAGE_MIN); report->pub.code = err - STD_ERR_BASE; report->refs = 1; report->gets = 1; if (m != NULL) { assert(m->report == NULL); m->report = report; report->refs++; } return &report->pub; } return NULL; } ErrorReport * create_error_report(void) { ReportBuffer * report = (ReportBuffer *)loc_alloc_zero(sizeof(ReportBuffer)); report->refs = 1; report->gets = 1; return &report->pub; } void release_error_report(ErrorReport * r) { if (r != NULL) { ReportBuffer * report = (ReportBuffer *)((char *)r - offsetof(ReportBuffer, pub)); assert(is_dispatch_thread()); assert(report->gets > 0); report->gets--; release_report(report); } } int compare_error_reports(ErrorReport * x, ErrorReport * y) { int i; if (x == y) return 1; if (x == NULL || y == NULL) return 0; if (x->code != y->code) return 0; if (x->format != y->format) { if (x->format == NULL || y->format == NULL) return 0; if (strcmp(x->format, y->format)) return 0; } if (x->param_cnt != y->param_cnt) return 0; for (i = 0; i < x->param_cnt; i++) { char * px = x->params[i]; char * py = y->params[i]; if (px != py) { if (px == NULL || py == NULL) return 0; if (strcmp(px, py)) return 0; } } if (x->props != y->props) { ErrorReportItem * px = x->props; ErrorReportItem * py = x->props; while (px != NULL || py != NULL) { if (px != py) { if (px == NULL || py == NULL) return 0; if (strcmp(px->name, py->name)) return 0; if (strcmp(px->value, py->value)) return 0; } px = px->next; py = py->next; } } return 1; } #ifdef NDEBUG void check_error(int error) { if (error == 0) return; #if ENABLE_Trace trace(LOG_ALWAYS, "Fatal error %d: %s", error, errno_to_str(error)); trace(LOG_ALWAYS, " Exiting agent..."); if (log_file == stderr) exit(1); #endif fprintf(stderr, "Fatal error %d: %s", error, errno_to_str(error)); fprintf(stderr, " Exiting agent..."); exit(1); } #else /* NDEBUG */ void check_error_debug(const char * file, int line, int error) { if (error == 0) return; #if ENABLE_Trace trace(LOG_ALWAYS, "Fatal error %d: %s", error, errno_to_str(error)); trace(LOG_ALWAYS, " At %s:%d", file, line); trace(LOG_ALWAYS, " Exiting agent..."); if (log_file == stderr) exit(1); #endif fprintf(stderr, "Fatal error %d: %s", error, errno_to_str(error)); fprintf(stderr, " At %s:%d", file, line); fprintf(stderr, " Exiting agent..."); exit(1); } #endif /* NDEBUG */