| /******************************************************************************* |
| * 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. |
| * |
| * Contributors: |
| * Wind River Systems - initial API and implementation |
| *******************************************************************************/ |
| |
| /* |
| * This module defines agent error codes in addition to system codes defined in errno.h |
| */ |
| |
| #include <config.h> |
| #include <stddef.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <assert.h> |
| #include <framework/errors.h> |
| #include <framework/events.h> |
| #include <framework/exceptions.h> |
| #include <framework/streams.h> |
| #include <framework/myalloc.h> |
| #include <framework/json.h> |
| #include <framework/trace.h> |
| |
| #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 <e32err.h> |
| |
| 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 */ |