From d2578295a953ced07371eedc885c032951b11297 Mon Sep 17 00:00:00 2001 From: Jakub Filak Date: Mon, 13 Jan 2014 16:38:17 +0100 Subject: [PATCH 30/39] Add an utility for stack trace analysis The tool is supposed to find all remote class paths and create a file named "not-reportale" containing a short explanation why the examined stack trace is not reportable via ABRT. The tool is designed for usage in post-create event. Related to #29 Related to rhbz#1054737 --- CMakeLists.txt | 19 ++- po/CMakeLists.txt | 64 ++++++++++ po/LINGUAS | 0 po/POTFILES.in | 1 + src/CMakeLists.txt | 7 +- utils/CMakeLists.txt | 34 ++++++ utils/abrt-action-analyze-java.c | 258 +++++++++++++++++++++++++++++++++++++++ utils/config.h.in | 2 + 8 files changed, 379 insertions(+), 6 deletions(-) create mode 100644 po/CMakeLists.txt create mode 100644 po/LINGUAS create mode 100644 po/POTFILES.in create mode 100644 utils/CMakeLists.txt create mode 100644 utils/abrt-action-analyze-java.c create mode 100644 utils/config.h.in diff --git a/CMakeLists.txt b/CMakeLists.txt index 1504461..33a5e03 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -39,6 +39,10 @@ endif() set(ARCHIVE_NAME ${CMAKE_PROJECT_NAME}-${PROJECT_VERSION}) +if(NOT BIN_INSTALL_DIR) + set(BIN_INSTALL_DIR ${CMAKE_INSTALL_PREFIX}/bin) +endif() + if(NOT LIB_INSTALL_DIR) set(LIB_INSTALL_DIR ${CMAKE_INSTALL_PREFIX}/lib) endif() @@ -55,10 +59,23 @@ if(NOT MAN_INSTALL_DIR) set(MAN_INSTALL_DIR ${SHARE_INSTALL_PREFIX}/man) endif() +if(NOT LOCALE_INSTALL_DIR) + set(LOCALE_INSTALL_DIR ${SHARE_INSTALL_PREFIX}/locale) +endif() + + add_custom_target( dist COMMAND git archive --prefix=${CMAKE_PROJECT_NAME}-${git_commit}/ HEAD | gzip > ${CMAKE_BINARY_DIR}/${ARCHIVE_NAME}.tar.gz WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} ) -subdirs(src etc test) +include(FindPkgConfig) +pkg_check_modules(PC_SATYR REQUIRED satyr) +pkg_check_modules(PC_LIBREPORT REQUIRED libreport) +pkg_check_modules(PC_ABRT REQUIRED abrt) + +add_definitions(-D_GNU_SOURCE) +set(AJC_ENABLE_NLS true) + +subdirs(src etc test utils po) diff --git a/po/CMakeLists.txt b/po/CMakeLists.txt new file mode 100644 index 0000000..7c49772 --- /dev/null +++ b/po/CMakeLists.txt @@ -0,0 +1,64 @@ +project(po) + +if (AJC_ENABLE_NLS) + find_program(INTLTOOL_UPDATE_CMD intltool-update) + mark_as_advanced(INTLTOOL_UPDATE_CMD) + + find_program(XGETTEXT_CMD xgettext) + mark_as_advanced(XGETTEXT_CMD) + + find_program(MSGMERGE_CMD msgmerge) + mark_as_advanced(MSGMERGE_CMD) + + find_program(MSGFMT_CMD msgfmt) + mark_as_advanced(MSGFMT_CMD) + + find_program(MSGFMT_CMD cat) + mark_as_advanced(CAT_CMD) + + file(STRINGS ${po_SOURCE_DIR}/LINGUAS AJC_LINGUAS) + set(AJC_LINGUAS_TARGETS) + set(AJC_POTFILE ${CMAKE_PROJECT_NAME}.pot) + set(AJC_POTFILE_OUTPUT ${po_BINARY_DIR}/${AJC_POTFILE}) + + add_custom_target(nls-update-sources + ${INTLTOOL_UPDATE_CMD} -m + WORKING_DIRECTORY ${po_SOURCE_DIR}) + + add_custom_command(OUTPUT ${AJC_POTFILE_OUTPUT} + COMMAND ${XGETTEXT_CMD} --files-from ${po_SOURCE_DIR}/POTFILES.in --keyword=_ -o ${AJC_POTFILE_OUTPUT} --copyright-holder="ABRT Team" --msgid-bugs-address="crash-catcher at lists.fedorahosted.org" --no-wrap --no-location + DEPENDS POTFILES.in + WORKING_DIRECTORY ${abrt-java-connector_SOURCE_DIR} + COMMENT "Extract translatable messages to ${AJC_POTFILE}" + ) + + foreach(language ${AJC_LINGUAS}) + set(language_SOURCE ${po_SOURCE_DIR}/${language}.po) + set(language_OUTPUT ${po_BINARY_DIR}/${language}/LC_MESSAGES/${CMAKE_PROJECT_NAME}.mo) + + add_custom_target(nls-update-${language}.po + ${MSGMERGE_CMD} ${language} ${AJC_POTFILE_OUTPUT} -o ${language_SOURCE} --no-wrap + DEPENDS ${language_SOURCE} ${AJC_POTFILE_OUTPUT} + WORKING_DIRECTORY ${po_SOURCE_DIR} + ) + + file(MAKE_DIRECTORY "${po_BINARY_DIR}/${language}/LC_MESSAGES") + + add_custom_command(OUTPUT ${language_OUTPUT} + COMMAND ${MSGFMT_CMD} -c -o ${language_OUTPUT} ${language_SOURCE} + DEPENDS ${language_SOURCE} + ) + + install(FILES ${language_OUTPUT} + DESTINATION share/${CMAKE_PROJECT_NAME}/locale/${language}/LC_MESSAGES + ) + + set(AJC_LINGUAS_TARGETS ${AJC_CATALOG_TARGETS} ${language_OUTPUT}) + endforeach(language) + + add_custom_target(nls ALL + DEPENDS ${AJC_POTFILE_OUTPUT} ${AJC_LINGUAS_TARGETS} + ) +else(AJC_ENABLE_NLS) + message("Native Language Support is disabled") +endif (AJC_ENABLE_NLS) diff --git a/po/LINGUAS b/po/LINGUAS new file mode 100644 index 0000000..e69de29 diff --git a/po/POTFILES.in b/po/POTFILES.in new file mode 100644 index 0000000..21aec67 --- /dev/null +++ b/po/POTFILES.in @@ -0,0 +1 @@ +utils/abrt-action-analyze-java.c diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index a00fe77..d084401 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,13 +1,10 @@ find_package(JNI REQUIRED) include_directories(${JAVA_INCLUDE_PATH} ${JAVA_INCLUDE_PATH2}) -include(FindPkgConfig) -pkg_check_modules(PC_ABRT REQUIRED libreport) pkg_check_modules(PC_JOURNALD REQUIRED libsystemd-journal) -include_directories(${PC_ABRT_INCLUDE_DIRS}) +include_directories(${PC_LIBREPORT_INCLUDE_DIRS}) include_directories(${PC_JOURNALD_INCLUDE_DIRS}) -add_definitions(-D_GNU_SOURCE) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -std=c99 -pedantic") set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -O0 -DVERBOSE") set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -DSILENT") @@ -20,7 +17,7 @@ set_target_properties( PROPERTIES OUTPUT_NAME abrt-java-connector) -target_link_libraries(AbrtChecker ${PC_ABRT_LIBRARIES}) +target_link_libraries(AbrtChecker ${PC_LIBREPORT_LIBRARIES}) target_link_libraries(AbrtChecker ${PC_JOURNALD_LIBRARIES}) install(TARGETS AbrtChecker DESTINATION ${LIB_INSTALL_DIR}) diff --git a/utils/CMakeLists.txt b/utils/CMakeLists.txt new file mode 100644 index 0000000..c358968 --- /dev/null +++ b/utils/CMakeLists.txt @@ -0,0 +1,34 @@ +project(utils) + +set(AbrtActionAnalyzeJava_SRCS abrt-action-analyze-java.c) + +include(CheckIncludeFiles) + +if (AJC_ENABLE_NLS) + check_include_files(locale.h HAVE_LOCALE_H) + if (HAVE_LOCALE_H) + set (ENABLE_NLS true) + endif (HAVE_LOCALE_H) +endif (AJC_ENABLE_NLS) + +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/config.h.in ${CMAKE_CURRENT_BINARY_DIR}/config.h) + +include_directories(${PC_SATYR_INCLUDE_DIRS}) +include_directories(${PC_LIBREPORT_INCLUDE_DIRS}) +include_directories(${PC_ABRT_INCLUDE_DIRS}) + +add_definitions(-DHAVE_CONFIG_H) +add_definitions(-DPACKAGE=\"${CMAKE_PROJECT_NAME}\") +add_definitions(-DLOCALEDIR=\"${LOCALE_INSTALL_DIR}\") +include_directories(${utils_BINARY_DIR}) + +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -std=gnu99 -pedantic") +set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -O0 -DVERBOSE") +set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -DSILENT") + +add_executable(abrt-action-analyze-java ${AbrtActionAnalyzeJava_SRCS}) +target_link_libraries(abrt-action-analyze-java ${PC_SATYR_LIBRARIES}) +target_link_libraries(abrt-action-analyze-java ${PC_LIBREPORT_LIBRARIES}) +target_link_libraries(abrt-action-analyze-java ${PC_ABRT_LIBRARIES}) + +install(TARGETS abrt-action-analyze-java DESTINATION ${BIN_INSTALL_DIR}) diff --git a/utils/abrt-action-analyze-java.c b/utils/abrt-action-analyze-java.c new file mode 100644 index 0000000..a4728b6 --- /dev/null +++ b/utils/abrt-action-analyze-java.c @@ -0,0 +1,258 @@ +/* + Copyright (C) 2014 Red Hat, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include +#include +#include +#include + +#include +#include + +static char * +backtrace_from_dump_dir(const char *dir_name) +{ + struct dump_dir *dd = dd_opendir(dir_name, DD_OPEN_READONLY); + if (NULL == dd) + { + return NULL; + } + + /* Read backtrace */ + /* Prints an error message if the file cannot be loaded */ + char *backtrace_str = dd_load_text_ext(dd, FILENAME_BACKTRACE, + DD_LOAD_TEXT_RETURN_NULL_ON_FAILURE); + + dd_close(dd); + + return backtrace_str; +} + +static void +write_not_reportable_message_to_dump_dir(const char *dir_name, const char *message) +{ + struct dump_dir *dd = dd_opendir(dir_name, /*Open for writing*/0); + if (NULL != dd) + { + dd_save_text(dd, FILENAME_NOT_REPORTABLE, message); + dd_close(dd); + } +} + +static void +write_not_reportable_message_to_fd(int fdout, const char *message) +{ + full_write(fdout, message, strlen(message)); + full_write(fdout, "\n", 1); +} + +static void +write_not_reportable_message_to_file(const char *file_name, const char *message) +{ + int fdout = open(file_name, + O_WRONLY | O_TRUNC | O_CREAT | O_NOFOLLOW, + S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP ); + + if (0 > fdout) + { + perror_msg("Can't open file '%s' for writing", file_name); + return; + } + write_not_reportable_message_to_fd(fdout, message); + close(fdout); +} + +static char * +backtrace_from_fd(int fdin) +{ + return xmalloc_read(fdin, /*no size limit*/NULL); +} + +static char * +backtrace_from_file(const char *file_name) +{ + return xmalloc_xopen_read_close(file_name, /*no size limit*/NULL); +} + +typedef void (*frame_cb)(struct sr_java_frame *frame, void *args); + +typedef struct { + frame_cb callback; + void *args; +} frame_proc_t; + +static void +iterate_trough_stacktrace(struct sr_java_stacktrace *stacktrace, frame_proc_t **fproc) +{ + struct sr_java_thread *thread = stacktrace->threads; + while (NULL != thread) + { + struct sr_java_frame *frame = thread->frames; + while (NULL != frame) + { + frame_proc_t **it = fproc; + while (NULL != *it) + { + (*it)->callback(frame, (*it)->args); + ++it; + } + frame = frame->next; + } + thread = thread->next; + } +} + +static void +work_out_list_of_remote_urls(struct sr_java_frame *frame, struct strbuf *remote_files_csv) +{ + if (NULL != frame->class_path && prefixcmp(frame->class_path, "file://") != 0) + { + struct stat buf; + if (stat(frame->class_path, &buf) && errno == ENOENT) + { + if (strstr(remote_files_csv->buf, frame->class_path) == NULL) + { + strbuf_append_strf(remote_files_csv, "%s%s", + remote_files_csv->buf[0] != '\0' ? ", " : "", + frame->class_path); + } + } + } +} + +int main(int argc, char *argv[]) +{ +#if ENABLE_NLS + /* I18n */ + setlocale(LC_ALL, ""); + bindtextdomain(PACKAGE, LOCALEDIR); + textdomain(PACKAGE); +#endif + + abrt_init(argv); + + const char *dump_dir_name = NULL; + const char *backtrace_file = NULL; + + /* Can't keep these strings/structs static: _() doesn't support that */ + const char *program_usage_string = _( + "& [[-d DIR] | [-f FILE]] [-o]\n" + "\n" + "Analyzes Java backtrace\n" + ); + enum { + OPT_v = 1 << 0, + OPT_d = 1 << 1, + OPT_f = 1 << 2, + OPT_o = 1 << 3, + }; + /* Keep enum above and order of options below in sync! */ + struct options program_options[] = { + OPT__VERBOSE(&g_verbose), + OPT_STRING('d', "dumpdir", &dump_dir_name, "DIR", _("Problem directory")), + OPT_STRING('f', "backtrace", &backtrace_file, "FILE", _("Path to backtrace")), + OPT_BOOL('o', "stdout", NULL, _("Print results on standard output")), + { 0 } + }; + program_options[ARRAY_SIZE(program_options) - 1].type = OPTION_END; + + unsigned opts = parse_opts(argc, argv, program_options, program_usage_string); + + export_abrt_envvars(0); + + if (NULL != dump_dir_name && NULL != backtrace_file) + error_msg_and_die("You need to pass either DIR or FILE"); + + int retval = 1; + char *backtrace_str = NULL; + if (NULL != dump_dir_name) + { + backtrace_str = backtrace_from_dump_dir(dump_dir_name); + } + else if (NULL != backtrace_file) + { + backtrace_str = backtrace_from_file(backtrace_file); + } + else + { + backtrace_str = backtrace_from_fd(STDIN_FILENO); + } + + if (NULL == backtrace_str) + goto finish; + + struct sr_location location; + sr_location_init(&location); + const char *backtrace_str_ptr = backtrace_str; + struct sr_java_stacktrace *stacktrace = sr_java_stacktrace_parse(&backtrace_str_ptr, &location); + free(backtrace_str); + + if (NULL == stacktrace) + { + error_msg("Could not parse the stack trace"); + goto finish; + } + + struct strbuf *remote_files_csv = strbuf_new(); + frame_proc_t remote_files_proc = { + .callback = (frame_cb)&work_out_list_of_remote_urls, + .args = (void *)remote_files_csv + }; + + frame_proc_t *fproc[] = { + &remote_files_proc, + //duphash_proc, + //backtrace_usability, + NULL, + }; + + iterate_trough_stacktrace(stacktrace, fproc); + + sr_java_stacktrace_free(stacktrace); + + if ('\0' != remote_files_csv->buf[0]) + { + char *not_reportable_message = xasprintf( + _("This problem can be caused by a 3rd party code from the "\ + "jar/class at %s. In order to provide valuable problem " \ + "reports, ABRT will not allow you to submit this problem. If you " \ + "still want to participate in solving this problem, please contact " \ + "the developers directly."), remote_files_csv->buf); + + if (opts & OPT_o) + { + write_not_reportable_message_to_fd(STDOUT_FILENO, not_reportable_message); + } + else if (NULL != dump_dir_name) + { + write_not_reportable_message_to_dump_dir(dump_dir_name, not_reportable_message); + } + else + { /* Just write it to the current working directory */ + write_not_reportable_message_to_file(FILENAME_NOT_REPORTABLE, not_reportable_message); + } + + free(not_reportable_message); + } + + strbuf_free(remote_files_csv); + retval = 0; +finish: + + return retval; +} diff --git a/utils/config.h.in b/utils/config.h.in new file mode 100644 index 0000000..6a26446 --- /dev/null +++ b/utils/config.h.in @@ -0,0 +1,2 @@ +#cmakedefine ENABLE_NLS 1 +#cmakedefine HAVE_LOCALE_H 1 -- 1.8.3.1