From 55d053118e68834564e238217f6511ad98e19b8d Mon Sep 17 00:00:00 2001 From: Eric Haszlakiewicz Date: Tue, 21 Apr 2020 03:51:16 +0000 Subject: [PATCH] Add an apps directory, and a json_parse program to parse an input file and report on memory usage. This is intended to provide a way, during development, to test out the memory and performance impacts of a change. --- CMakeLists.txt | 6 ++ ChangeLog | 2 + apps/CMakeLists.txt | 11 +++ apps/json_parse.c | 175 ++++++++++++++++++++++++++++++++++++++++++++ cmake/config.h.in | 6 ++ 5 files changed, 200 insertions(+) create mode 100644 apps/CMakeLists.txt create mode 100644 apps/json_parse.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 1d09e50..4d4cc9b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -40,6 +40,8 @@ if (CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME AND BUILD_TESTING AND add_subdirectory(tests) endif() +add_subdirectory(apps) + # Set some packaging variables. set(CPACK_PACKAGE_NAME "${PROJECT_NAME}") set(CPACK_PACKAGE_VERSION_MAJOR "${PROJECT_VERSION_MAJOR}") @@ -108,6 +110,7 @@ check_include_files("stdlib.h;stdarg.h;string.h;float.h" STDC_HEADERS) check_include_file(unistd.h HAVE_UNISTD_H) check_include_file(sys/types.h HAVE_SYS_TYPES_H) +check_include_file(sys/resource.h HAVE_SYS_RESOURCE_H) # for getrusage check_include_file("dlfcn.h" HAVE_DLFCN_H) check_include_file("endian.h" HAVE_ENDIAN_H) @@ -165,6 +168,9 @@ endif() if (HAVE_SYSLOG_H) check_symbol_exists(vsyslog "syslog.h" HAVE_VSYSLOG) endif() +if (HAVE_SYS_RESOURCE_H) + check_symbol_exists(getrusage "sys/resource.h" HAVE_GETRUSAGE) +endif() check_symbol_exists(strtoll "stdlib.h" HAVE_STRTOLL) check_symbol_exists(strtoull "stdlib.h" HAVE_STRTOULL) diff --git a/ChangeLog b/ChangeLog index 6b41ab1..8a58d19 100644 --- a/ChangeLog +++ b/ChangeLog @@ -8,6 +8,8 @@ Deprecated and removed features: Other changes -------------- +* Add a json_parse binary, for use in testing changes (not installed). +* Issue #471: always create directories with mode 0755, regardless of umask. * Added a JSON_TOKENER_ALLOW_TRAILING_CHARS flag to allow multiple objects to be parsed even when JSON_TOKENER_STRICT is set. diff --git a/apps/CMakeLists.txt b/apps/CMakeLists.txt new file mode 100644 index 0000000..dda68a2 --- /dev/null +++ b/apps/CMakeLists.txt @@ -0,0 +1,11 @@ + +# Note: it is intentional that there are no install instructions here yet. +# When/if the interface of the app(s) listed here settles down enough to +# publish as part of a regular build that will be added. + +add_executable(json_parse json_parse.c) +#target_compile_definitions(json_parse PRIVATE TEST_FORMATTED=1) +target_link_libraries(json_parse PRIVATE ${PROJECT_NAME}) + +include_directories(PUBLIC ${CMAKE_SOURCE_DIR}) + diff --git a/apps/json_parse.c b/apps/json_parse.c new file mode 100644 index 0000000..754666e --- /dev/null +++ b/apps/json_parse.c @@ -0,0 +1,175 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "config.h" +#include "json_object.h" +#include "json_tokener.h" +#include "json_util.h" + +#ifdef HAVE_SYS_RESOURCE_H +#include +#include +#endif + +static int formatted_output = 0; +static int show_output = 1; +static int strict_mode = 0; +static const char *fname = NULL; + +static void usage(int exitval, const char *errmsg); +static void showmem(void); +static int parseit(int fd, int (*callback)(struct json_object *)); +static int showobj(struct json_object *new_obj); + +static void showmem(void) +{ +#ifdef HAVE_GETRUSAGE + struct rusage rusage; + memset(&rusage, 0, sizeof(rusage)); + getrusage(RUSAGE_SELF, &rusage); + printf("maxrss: %d KB\n", rusage.ru_maxrss); +#endif +} + +static int parseit(int fd, int (*callback)(struct json_object *)) +{ + struct json_object *obj; + char buf[32768]; + int ret; + int depth = JSON_TOKENER_DEFAULT_DEPTH; + json_tokener *tok; + + tok = json_tokener_new_ex(depth); + if (!tok) + { + fprintf(stderr, "unable to allocate json_tokener: %s\n", strerror(errno)); + return 1; + } + json_tokener_set_flags(tok, JSON_TOKENER_STRICT | JSON_TOKENER_ALLOW_TRAILING_CHARS); + + // XXX push this into some kind of json_tokener_parse_fd API? + // json_object_from_fd isn't flexible enough, and mirroring + // everything you can do with a tokener into json_util.c seems + // like the wrong approach. + size_t total_read = 0; + while ((ret = read(fd, buf, sizeof(buf))) > 0) + { + total_read += ret; + int start_pos = 0; + while (start_pos != ret) + { + obj = json_tokener_parse_ex(tok, &buf[start_pos], ret - start_pos); + enum json_tokener_error jerr = json_tokener_get_error(tok); + int parse_end = json_tokener_get_parse_end(tok); + if (obj == NULL && jerr != json_tokener_continue) + { + char *aterr = &buf[start_pos + parse_end]; + fflush(stdout); + int fail_offset = total_read - ret + start_pos + parse_end; + fprintf(stderr, "Failed at offset %d: %s %c\n", fail_offset, + json_tokener_error_desc(jerr), aterr[0]); + json_tokener_free(tok); + return 1; + } + if (obj != NULL) + { + int cb_ret = callback(obj); + json_object_put(obj); + if (cb_ret != 0) + { + json_tokener_free(tok); + return 1; + } + } + start_pos += json_tokener_get_parse_end(tok); + assert(start_pos <= ret); + } + } + if (ret < 0) + { + fprintf(stderr, "error reading fd %d: %s\n", fd, strerror(errno)); + } + + json_tokener_free(tok); + return 0; +} + +static int showobj(struct json_object *new_obj) +{ + if (new_obj == NULL) + { + fprintf(stderr, "%s: Failed to parse\n", fname); + return 1; + } + + printf("Successfully parsed object from %s\n", fname); + + if (show_output) + { + const char *output; + if (formatted_output) + output = json_object_to_json_string(new_obj); + else + output = json_object_to_json_string_ext(new_obj, JSON_C_TO_STRING_PRETTY); + printf("%s\n", output); + } + + showmem(); + return 0; +} + +static void usage(int exitval, const char *errmsg) +{ + FILE *fp = stdout; + if (exitval != 0) + fp = stderr; + if (errmsg != NULL) + fprintf(fp, "ERROR: %s\n\n", errmsg); + fprintf(fp, "Usage: %s [-f] [-n] [-s]\n"); + fprintf(fp, " -f - Format the output with JSON_C_TO_STRING_PRETTY\n"); + fprintf(fp, " -n - No output\n"); + fprintf(fp, " -s - Parse in strict mode, flags:\n"); + fprintf(fp, " JSON_TOKENER_STRICT|JSON_TOKENER_ALLOW_TRAILING_CHARS\n"); + + fprintf(fp, "\nWARNING WARNING WARNING\n"); + fprintf(fp, "This is a prototype, it may change or be removed at any time!\n"); + exit(exitval); +} + +int main(int argc, char **argv) +{ + json_object *new_obj; + int opt; + + while ((opt = getopt(argc, argv, "fns")) != -1) + { + switch (opt) + { + case 'f': formatted_output = 1; break; + case 'n': show_output = 0; break; + case 's': strict_mode = 1; break; + default: /* '?' */ fprintf(stderr, "Usage: %s [-f]\n", argv[0]); exit(EXIT_FAILURE); + } + } + + if (optind >= argc) + { + fprintf(stderr, "Expected argument after options\n"); + exit(EXIT_FAILURE); + } + fname = argv[optind]; + int fd = open(argv[optind], O_RDONLY, 0); + showmem(); + if (parseit(fd, showobj) != 0) + exit(EXIT_FAILURE); + showmem(); + + exit(EXIT_SUCCESS); +} diff --git a/cmake/config.h.in b/cmake/config.h.in index 5e64e58..9c65082 100644 --- a/cmake/config.h.in +++ b/cmake/config.h.in @@ -54,6 +54,9 @@ /* Define to 1 if you have the header file. */ #cmakedefine HAVE_SYS_PARAM_H @HAVE_SYS_PARAM_H@ +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_SYS_RESOURCE_H + /* Define to 1 if you have the header file. */ #cmakedefine HAVE_SYS_STAT_H @@ -135,6 +138,9 @@ /* Define to 1 if you have the `vsyslog' function. */ #cmakedefine HAVE_VSYSLOG @HAVE_VSYSLOG@ +/* Define if you have the `getrusage' function. */ +#cmakedefine HAVE_GETRUSAGE + #cmakedefine HAVE_STRTOLL #if !defined(HAVE_STRTOLL) #define strtoll @json_c_strtoll@ -- 2.50.1