| /* |
| * Copyright © 2015 Samsung Electronics Co., Ltd |
| * |
| * Permission is hereby granted, free of charge, to any person obtaining |
| * a copy of this software and associated documentation files (the |
| * "Software"), to deal in the Software without restriction, including |
| * without limitation the rights to use, copy, modify, merge, publish, |
| * distribute, sublicense, and/or sell copies of the Software, and to |
| * permit persons to whom the Software is furnished to do so, subject to |
| * the following conditions: |
| * |
| * The above copyright notice and this permission notice (including the |
| * next paragraph) shall be included in all copies or substantial |
| * portions of the Software. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
| * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
| * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
| * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS |
| * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN |
| * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN |
| * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
| * SOFTWARE. |
| */ |
| |
| #include "config.h" |
| |
| #include "zuc_collector.h" |
| |
| #include <stdint.h> |
| #include <stdio.h> |
| #include <string.h> |
| #include <unistd.h> |
| |
| #include "shared/zalloc.h" |
| #include "zuc_event_listener.h" |
| #include "zunitc/zunitc_impl.h" |
| |
| #include <sys/types.h> |
| #include <sys/stat.h> |
| #include <fcntl.h> |
| |
| /** |
| * @file |
| * General handling of collecting events during testing to pass back to |
| * main tracking of fork()'d tests. |
| * |
| * @note implementation of zuc_process_message() is included here so that |
| * all child->parent IPC is in a single source file for easier maintenance |
| * and updating. |
| */ |
| |
| /** |
| * Internal data struct for processing. |
| */ |
| struct collector_data |
| { |
| int *fd; /**< file descriptor to output to. */ |
| struct zuc_test *test; /**< current test, or NULL. */ |
| }; |
| |
| /** |
| * Stores an int32_t into the given buffer. |
| * |
| * @param ptr the buffer to store to. |
| * @param val the value to store. |
| * @return a pointer to the position in the buffer after the stored value. |
| */ |
| static char * |
| pack_int32(char *ptr, int32_t val); |
| |
| /** |
| * Stores an intptr_t into the given buffer. |
| * |
| * @param ptr the buffer to store to. |
| * @param val the value to store. |
| * @return a pointer to the position in the buffer after the stored value. |
| */ |
| static char * |
| pack_intptr_t(char *ptr, intptr_t val); |
| |
| /** |
| * Extracts a int32_t from the given buffer. |
| * |
| * @param ptr the buffer to extract from. |
| * @param val the value to set. |
| * @return a pointer to the position in the buffer after the extracted |
| * value. |
| */ |
| static char const * |
| unpack_int32(char const *ptr, int32_t *val); |
| |
| /** |
| * Extracts a intptr_t from the given buffer. |
| * |
| * @param ptr the buffer to extract from. |
| * @param val the value to set. |
| * @return a pointer to the position in the buffer after the extracted |
| * value. |
| */ |
| static char const * |
| unpack_intptr_t(char const *ptr, intptr_t *val); |
| |
| /** |
| * Extracts a length-prefixed string from the given buffer. |
| * |
| * @param ptr the buffer to extract from. |
| * @param str the value to set. |
| * @return a pointer to the position in the buffer after the extracted |
| * value. |
| */ |
| static char const * |
| unpack_string(char const *ptr, char **str); |
| |
| /** |
| * Extracts an event from the given buffer. |
| * |
| * @param ptr the buffer to extract from. |
| * @param len the length of the given buffer. |
| * @return an event that was packed in the buffer |
| */ |
| static struct zuc_event * |
| unpack_event(char const *ptr, int32_t len); |
| |
| /** |
| * Handles an event by either attaching it directly or sending it over IPC |
| * as needed. |
| */ |
| static void |
| store_event(struct collector_data *cdata, |
| enum zuc_event_type event_type, char const *file, int line, |
| enum zuc_fail_state state, enum zuc_check_op op, |
| enum zuc_check_valtype valtype, |
| intptr_t val1, intptr_t val2, const char *expr1, const char *expr2); |
| |
| static void |
| destroy(void *data); |
| |
| static void |
| test_started(void *data, struct zuc_test *test); |
| |
| static void |
| test_ended(void *data, struct zuc_test *test); |
| |
| static void |
| check_triggered(void *data, char const *file, int line, |
| enum zuc_fail_state state, enum zuc_check_op op, |
| enum zuc_check_valtype valtype, |
| intptr_t val1, intptr_t val2, |
| const char *expr1, const char *expr2); |
| |
| static void |
| collect_event(void *data, char const *file, int line, const char *expr1); |
| |
| struct zuc_event_listener * |
| zuc_collector_create(int *pipe_fd) |
| { |
| struct zuc_event_listener *listener = |
| zalloc(sizeof(struct zuc_event_listener)); |
| |
| listener->data = zalloc(sizeof(struct collector_data)); |
| ((struct collector_data *)listener->data)->fd = pipe_fd; |
| listener->destroy = destroy; |
| listener->test_started = test_started; |
| listener->test_ended = test_ended; |
| listener->check_triggered = check_triggered; |
| listener->collect_event = collect_event; |
| |
| return listener; |
| } |
| |
| char * |
| pack_int32(char *ptr, int32_t val) |
| { |
| memcpy(ptr, &val, sizeof(val)); |
| return ptr + sizeof(val); |
| } |
| |
| char * |
| pack_intptr_t(char *ptr, intptr_t val) |
| { |
| memcpy(ptr, &val, sizeof(val)); |
| return ptr + sizeof(val); |
| } |
| |
| static char * |
| pack_cstr(char *ptr, intptr_t val, int len) |
| { |
| if (val == 0) { /* a null pointer */ |
| ptr = pack_int32(ptr, -1); |
| } else { |
| ptr = pack_int32(ptr, len); |
| memcpy(ptr, (const char *)val, len); |
| ptr += len; |
| } |
| return ptr; |
| } |
| |
| void |
| destroy(void *data) |
| { |
| free(data); |
| } |
| |
| void |
| test_started(void *data, struct zuc_test *test) |
| { |
| struct collector_data *cdata = data; |
| cdata->test = test; |
| } |
| |
| void |
| test_ended(void *data, struct zuc_test *test) |
| { |
| struct collector_data *cdata = data; |
| cdata->test = NULL; |
| } |
| |
| void |
| check_triggered(void *data, char const *file, int line, |
| enum zuc_fail_state state, enum zuc_check_op op, |
| enum zuc_check_valtype valtype, |
| intptr_t val1, intptr_t val2, |
| const char *expr1, const char *expr2) |
| { |
| struct collector_data *cdata = data; |
| if (op != ZUC_OP_TRACEPOINT) |
| store_event(cdata, ZUC_EVENT_IMMEDIATE, file, line, state, op, |
| valtype, |
| val1, val2, expr1, expr2); |
| } |
| |
| void |
| collect_event(void *data, char const *file, int line, const char *expr1) |
| { |
| struct collector_data *cdata = data; |
| store_event(cdata, ZUC_EVENT_DEFERRED, file, line, ZUC_CHECK_OK, |
| ZUC_OP_TRACEPOINT, ZUC_VAL_INT, |
| 0, 0, expr1, ""); |
| } |
| |
| void |
| store_event(struct collector_data *cdata, |
| enum zuc_event_type event_type, char const *file, int line, |
| enum zuc_fail_state state, enum zuc_check_op op, |
| enum zuc_check_valtype valtype, |
| intptr_t val1, intptr_t val2, const char *expr1, const char *expr2) |
| { |
| struct zuc_event *event = zalloc(sizeof(*event)); |
| event->file = strdup(file); |
| event->line = line; |
| event->state = state; |
| event->op = op; |
| event->valtype = valtype; |
| event->val1 = val1; |
| event->val2 = val2; |
| if (valtype == ZUC_VAL_CSTR) { |
| if (val1) |
| event->val1 = (intptr_t)strdup((const char *)val1); |
| if (val2) |
| event->val2 = (intptr_t)strdup((const char *)val2); |
| } |
| event->expr1 = strdup(expr1); |
| event->expr2 = strdup(expr2); |
| |
| zuc_attach_event(cdata->test, event, event_type, false); |
| |
| if (*cdata->fd == -1) { |
| } else { |
| /* Need to pass it back */ |
| int sent; |
| int count; |
| int expr1_len = strlen(expr1); |
| int expr2_len = strlen(expr2); |
| int val1_len = |
| ((valtype == ZUC_VAL_CSTR) && val1) ? |
| strlen((char *)val1) : 0; |
| int val2_len = |
| ((valtype == ZUC_VAL_CSTR) && val2) ? |
| strlen((char *)val2) : 0; |
| int file_len = strlen(file); |
| int len = (sizeof(int32_t) * 9) + file_len |
| + (sizeof(intptr_t) * 2) |
| + ((valtype == ZUC_VAL_CSTR) ? |
| (sizeof(int32_t) * 2) + val1_len + val2_len : 0) |
| + expr1_len + expr2_len; |
| char *buf = zalloc(len); |
| |
| char *ptr = pack_int32(buf, len - 4); |
| ptr = pack_int32(ptr, event_type); |
| ptr = pack_int32(ptr, file_len); |
| memcpy(ptr, file, file_len); |
| ptr += file_len; |
| ptr = pack_int32(ptr, line); |
| ptr = pack_int32(ptr, state); |
| ptr = pack_int32(ptr, op); |
| ptr = pack_int32(ptr, valtype); |
| if (valtype == ZUC_VAL_CSTR) { |
| ptr = pack_cstr(ptr, val1, val1_len); |
| ptr = pack_cstr(ptr, val2, val2_len); |
| } else { |
| ptr = pack_intptr_t(ptr, val1); |
| ptr = pack_intptr_t(ptr, val2); |
| } |
| |
| ptr = pack_int32(ptr, expr1_len); |
| if (expr1_len) { |
| memcpy(ptr, expr1, expr1_len); |
| ptr += expr1_len; |
| } |
| ptr = pack_int32(ptr, expr2_len); |
| if (expr2_len) { |
| memcpy(ptr, expr2, expr2_len); |
| ptr += expr2_len; |
| } |
| |
| |
| sent = 0; |
| while (sent < len) { |
| count = write(*cdata->fd, buf, len); |
| if (count == -1) |
| break; |
| sent += count; |
| } |
| |
| free(buf); |
| } |
| } |
| |
| char const * |
| unpack_int32(char const *ptr, int32_t *val) |
| { |
| memcpy(val, ptr, sizeof(*val)); |
| return ptr + sizeof(*val); |
| } |
| |
| char const * |
| unpack_intptr_t(char const *ptr, intptr_t *val) |
| { |
| memcpy(val, ptr, sizeof(*val)); |
| return ptr + sizeof(*val); |
| } |
| |
| char const * |
| unpack_string(char const *ptr, char **str) |
| { |
| int32_t len = 0; |
| ptr = unpack_int32(ptr, &len); |
| *str = zalloc(len + 1); |
| if (len) |
| memcpy(*str, ptr, len); |
| ptr += len; |
| return ptr; |
| } |
| |
| static char const * |
| unpack_cstr(char const *ptr, char **str) |
| { |
| int32_t len = 0; |
| ptr = unpack_int32(ptr, &len); |
| if (len < 0) { |
| *str = NULL; |
| } else { |
| *str = zalloc(len + 1); |
| if (len) |
| memcpy(*str, ptr, len); |
| ptr += len; |
| } |
| return ptr; |
| } |
| |
| struct zuc_event * |
| unpack_event(char const *ptr, int32_t len) |
| { |
| int32_t val = 0; |
| struct zuc_event *evt = zalloc(sizeof(*evt)); |
| char const *tmp = unpack_string(ptr, &evt->file); |
| tmp = unpack_int32(tmp, &evt->line); |
| |
| tmp = unpack_int32(tmp, &val); |
| evt->state = val; |
| tmp = unpack_int32(tmp, &val); |
| evt->op = val; |
| tmp = unpack_int32(tmp, &val); |
| evt->valtype = val; |
| |
| if (evt->valtype == ZUC_VAL_CSTR) { |
| char *ptr = NULL; |
| tmp = unpack_cstr(tmp, &ptr); |
| evt->val1 = (intptr_t)ptr; |
| tmp = unpack_cstr(tmp, &ptr); |
| evt->val2 = (intptr_t)ptr; |
| } else { |
| tmp = unpack_intptr_t(tmp, &evt->val1); |
| tmp = unpack_intptr_t(tmp, &evt->val2); |
| } |
| |
| tmp = unpack_string(tmp, &evt->expr1); |
| tmp = unpack_string(tmp, &evt->expr2); |
| |
| return evt; |
| } |
| |
| int |
| zuc_process_message(struct zuc_test *test, int fd) |
| { |
| char buf[4] = {}; |
| int got = read(fd, buf, 4); |
| if (got == 4) { |
| enum zuc_event_type event_type = ZUC_EVENT_IMMEDIATE; |
| int32_t val = 0; |
| int32_t len = 0; |
| const char *tmp = NULL; |
| char *raw = NULL; |
| unpack_int32(buf, &len); |
| raw = zalloc(len); |
| got = read(fd, raw, len); |
| |
| tmp = unpack_int32(raw, &val); |
| event_type = val; |
| |
| struct zuc_event *evt = unpack_event(tmp, len - (tmp - raw)); |
| zuc_attach_event(test, evt, event_type, true); |
| free(raw); |
| } |
| return got; |
| } |