From 96439591a8be7e3e10a5de9488dde245777318c8 Mon Sep 17 00:00:00 2001 From: Sven Hassler Date: Thu, 19 Nov 2015 14:20:11 +0100 Subject: First implementation of dlt-procfs This tool logs the following information to dlt-daemon: - PID, parent's PID, commandline when new processes appear, maked by "NEW" - PID when processes stop, marked by "STP" - PID, CPU-Time (ms), RSS (bytes), CTX-switches, I/O (bytes), I/O-wait (ms) for all processes that consume CPU time, marked as "ACT" - PID, commandline in a regular time-interval, marked as "CHK" - The number of interrupts on each CPU in a regular time interval, marked as "IRQ" A configuration file, called dlt-procfs.conf allows configuring the time intervals of the updated processes, the command-line updates and the interrupts as well as the preferred log level. --- CMakeLists.txt | 2 + src/CMakeLists.txt | 4 + src/procfs/CMakeLists.txt | 29 ++ src/procfs/dlt-procfs-common.c | 68 +++++ src/procfs/dlt-procfs-common.h | 40 +++ src/procfs/dlt-procfs-interrupt.c | 147 ++++++++++ src/procfs/dlt-procfs-interrupt.h | 35 +++ src/procfs/dlt-procfs-options.c | 259 ++++++++++++++++++ src/procfs/dlt-procfs-process-list.c | 288 ++++++++++++++++++++ src/procfs/dlt-procfs-process-list.h | 54 ++++ src/procfs/dlt-procfs-process.c | 467 ++++++++++++++++++++++++++++++++ src/procfs/dlt-procfs-process.h | 56 ++++ src/procfs/dlt-procfs.c | 503 +++++++++++++++++++++++++++++++++++ src/procfs/dlt-procfs.conf | 18 ++ src/procfs/dlt-procfs.h | 61 +++++ 15 files changed, 2031 insertions(+) create mode 100644 src/procfs/CMakeLists.txt create mode 100644 src/procfs/dlt-procfs-common.c create mode 100644 src/procfs/dlt-procfs-common.h create mode 100644 src/procfs/dlt-procfs-interrupt.c create mode 100644 src/procfs/dlt-procfs-interrupt.h create mode 100644 src/procfs/dlt-procfs-options.c create mode 100644 src/procfs/dlt-procfs-process-list.c create mode 100644 src/procfs/dlt-procfs-process-list.h create mode 100644 src/procfs/dlt-procfs-process.c create mode 100644 src/procfs/dlt-procfs-process.h create mode 100644 src/procfs/dlt-procfs.c create mode 100644 src/procfs/dlt-procfs.conf create mode 100644 src/procfs/dlt-procfs.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 52edb51..28c1b18 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -73,6 +73,7 @@ option(WITH_DLT_COREDUMPHANDLER "EXPERIMENTAL! Set to ON to build src/core_d option(WITH_DLT_LOGSTORAGE_CTRL_UDEV "PROTOTYPE! Set to ON to build logstorage control application with udev support" OFF) option(WITH_DLT_LOGSTORAGE_CTRL_PROP "PROTOTYPE! Set to ON to build logstorage control application with proprietary support" OFF) option(WITH_DLT_USE_IPv6 "Set to ON for IPv6 support" ON) +option(WITH_DLT_PROCFS "Set to ON to build src/procfs binaries" ON) # RPM settings set( GENIVI_RPM_RELEASE "1")#${DLT_REVISION}") set( LICENSE "Mozilla Public License Version 2.0" ) @@ -206,6 +207,7 @@ message( STATUS "WITH_DLT_SHM_ENABLE = ${WITH_DLT_SHM_ENABLE}" ) message( STATUS "WITH_DLTTEST = ${WITH_DLTTEST}" ) message( STATUS "WITH_DLT_CXX11_EXT = ${WITH_DLT_CXX11_EXT}" ) message( STATUS "WITH_DLT_COREDUMPHANDLER = ${WITH_DLT_COREDUMPHANDLER}" ) +message( STATUS "WITH_DLT_PROCFS = ${WITH_DLT_PROCFS}" ) message( STATUS "WITH_CHECK_CONFIG_FILE = ${WITH_CHECK_CONFIG_FILE}" ) message( STATUS "WITH_TESTSCRIPTS = ${WITH_TESTSCRIPTS}" ) message( STATUS "WITH_GPROF = ${WITH_GPROF}" ) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 6fd8a64..f30932a 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -46,3 +46,7 @@ endif( WITH_DLT_DBUS ) if( WITH_DLT_COREDUMPHANDLER ) add_subdirectory( core_dump_handler ) endif( WITH_DLT_COREDUMPHANDLER ) + +if( WITH_DLT_PROCFS ) + add_subdirectory( procfs ) +endif( WITH_DLT_PROCFS ) diff --git a/src/procfs/CMakeLists.txt b/src/procfs/CMakeLists.txt new file mode 100644 index 0000000..bbe3d49 --- /dev/null +++ b/src/procfs/CMakeLists.txt @@ -0,0 +1,29 @@ +####### +# @licence make begin@ +# SPDX license identifier: MPL-2.0 +# +# Copyright (C) 2011-2015, BMW AG +# +# This file is part of GENIVI Project DLT - Diagnostic Log and Trace. +# +# This Source Code Form is subject to the terms of the +# Mozilla Public License (MPL), v. 2.0. +# If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. +# +# For further information see http://www.genivi.org/. +# @licence end@ +####### + +set (dlt_procfs_SRCS dlt-procfs.c dlt-procfs-options.c dlt-procfs-process.c dlt-procfs-process-list.c dlt-procfs-common.c dlt-procfs-interrupt.c) +add_executable (dlt-procfs ${dlt_procfs_SRCS}) +target_link_libraries (dlt-procfs dlt) +set_target_properties(dlt-procfs PROPERTIES LINKER_LANGUAGE C) + +install(TARGETS dlt-procfs + RUNTIME DESTINATION bin + COMPONENT base) + +INSTALL(FILES dlt-procfs.conf + DESTINATION ${CONFIGURATION_FILES_DIR} + COMPONENT base) diff --git a/src/procfs/dlt-procfs-common.c b/src/procfs/dlt-procfs-common.c new file mode 100644 index 0000000..c7e490d --- /dev/null +++ b/src/procfs/dlt-procfs-common.c @@ -0,0 +1,68 @@ +/* + * @licence app begin@ + * SPDX license identifier: MPL-2.0 + * + * Copyright (C) 2011-2015, BMW AG + * + * This file is part of GENIVI Project DLT - Diagnostic Log and Trace. + * + * This Source Code Form is subject to the terms of the + * Mozilla Public License (MPL), v. 2.0. + * If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + * + * For further information see http://www.genivi.org/. + * @licence end@ + */ + +/*! + * \author Sven Hassler + * + * \copyright Copyright © 2011-2015 BMW AG. \n + * License MPL-2.0: Mozilla Public License version 2.0 http://mozilla.org/MPL/2.0/. + * + * \file dlt-procfs-common.c + */ + +#include "dlt-procfs-common.h" + +DltReturnValue dlt_procfs_read_file_compact(char *filename, char **target) +{ + char buffer[BUFFER_SIZE]; + int ret = dlt_procfs_read_file(filename, buffer, BUFFER_SIZE); + if(ret < DLT_RETURN_OK) + return ret; + + if((*target = malloc(strlen(buffer) + 1)) == NULL) + { + fprintf(stderr, "Out of memory!\n"); + return DLT_RETURN_ERROR; + } + + memcpy(*target, buffer, strlen(buffer) + 1); + + return DLT_RETURN_OK; +} + +DltReturnValue dlt_procfs_read_file(char* filename, char* buffer, uint maxLength) +{ + if(filename == NULL || buffer == NULL) + { + fprintf(stderr, "Nullpointer parameter!\n"); + return DLT_RETURN_WRONG_PARAMETER; + } + + FILE* file = fopen(filename, "r"); + if(file == NULL) + { + // fprintf(stderr, "Could not read file %s\n", filename); + return DLT_RETURN_ERROR; + } + + int buflen = fread(buffer, 1, maxLength-1, file); + buffer[buflen] = '\0'; + + fclose(file); + + return DLT_RETURN_OK; +} diff --git a/src/procfs/dlt-procfs-common.h b/src/procfs/dlt-procfs-common.h new file mode 100644 index 0000000..ed7f096 --- /dev/null +++ b/src/procfs/dlt-procfs-common.h @@ -0,0 +1,40 @@ +/* + * @licence app begin@ + * SPDX license identifier: MPL-2.0 + * + * Copyright (C) 2011-2015, BMW AG + * + * This file is part of GENIVI Project DLT - Diagnostic Log and Trace. + * + * This Source Code Form is subject to the terms of the + * Mozilla Public License (MPL), v. 2.0. + * If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + * + * For further information see http://www.genivi.org/. + * @licence end@ + */ + +/*! + * \author Sven Hassler + * + * \copyright Copyright © 2011-2015 BMW AG. \n + * License MPL-2.0: Mozilla Public License version 2.0 http://mozilla.org/MPL/2.0/. + * + * \file dlt-procfs-common.h + */ + +#ifndef SRC_PROCFS_DLT_PROCFS_COMMON_H_ +#define SRC_PROCFS_DLT_PROCFS_COMMON_H_ + +#include +#include +#include +#include + +#define BUFFER_SIZE 4096 + +DltReturnValue dlt_procfs_read_file(char* filename, char* buffer, uint maxLength); +DltReturnValue dlt_procfs_read_file_compact(char *filename, char **target); + +#endif /* SRC_PROCFS_DLT_PROCFS_COMMON_H_ */ diff --git a/src/procfs/dlt-procfs-interrupt.c b/src/procfs/dlt-procfs-interrupt.c new file mode 100644 index 0000000..10d8823 --- /dev/null +++ b/src/procfs/dlt-procfs-interrupt.c @@ -0,0 +1,147 @@ +/* + * @licence app begin@ + * SPDX license identifier: MPL-2.0 + * + * Copyright (C) 2011-2015, BMW AG + * + * This file is part of GENIVI Project DLT - Diagnostic Log and Trace. + * + * This Source Code Form is subject to the terms of the + * Mozilla Public License (MPL), v. 2.0. + * If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + * + * For further information see http://www.genivi.org/. + * @licence end@ + */ + +/*! + * \author Sven Hassler + * + * \copyright Copyright © 2011-2015 BMW AG. \n + * License MPL-2.0: Mozilla Public License version 2.0 http://mozilla.org/MPL/2.0/. + * + * \file dlt-procfs-interrupt.c + */ + +#include "dlt-procfs-interrupt.h" + +DltReturnValue dlt_procfs_log_interrupts(DltContext *ctx, DltLogLevelType log_level) +{ + if(ctx == NULL) + { + fprintf(stderr, "dlt_procfs_log_interrupts(): Nullpointer parameter\n"); + return DLT_RETURN_WRONG_PARAMETER; + } + + char buffer[BUFFER_SIZE]; + *buffer = '\0'; + + char file_buffer[BUFFER_SIZE]; + char *token, *delim = " \t", *delim2 = " \t\n", *check; + int head_line = 1, first_row = 1, cpu_count = 0, column = 0, buffer_offset = 0; + DltReturnValue ret; + + if((ret = dlt_procfs_read_file("/proc/interrupts", file_buffer, BUFFER_SIZE)) < DLT_RETURN_OK) return ret; + + token = strtok(file_buffer, delim); + while(token != NULL) + { + if(head_line) + { + if(strlen(token) > 3 && token[0]=='C' && token[1]=='P' && token[2]=='U') + cpu_count++; + else if(cpu_count <= 0) + { + fprintf(stderr, "dlt_procfs_log_interrupts: Could not parse CPU count\n"); + return DLT_RETURN_ERROR; + } + else if(strcmp(token, "\n") == 0) + head_line = 0; + + token = strtok(NULL, delim); + } + else + { + int tokenlen = strlen(token); + if(token[tokenlen - 1] == ':') + { + column = 0; + + if(first_row) + first_row = 0; + else + buffer_offset += snprintf(buffer + buffer_offset, BUFFER_SIZE - buffer_offset, "\n"); + } + + if(column == 0) // IRQ number + { + buffer_offset += snprintf(buffer + buffer_offset, BUFFER_SIZE - buffer_offset, "%.*s;", tokenlen-1, token); + } + else if(column <= cpu_count) + { + long int interrupt_count = strtol(token, &check, 10); + if(*check != '\0') + { + fprintf(stderr, "dlt_procfs_log_interrupts: Could not parse interrupt count for CPU %d\n", column - 1); + return DLT_RETURN_ERROR; + } + + buffer_offset += snprintf(buffer + buffer_offset, BUFFER_SIZE - buffer_offset, "cpu%d:%ld;", column - 1, interrupt_count); + } + + column++; + + token = strtok(NULL, delim2); + } + } + + DltContextData ctx_data; + if((ret = dlt_user_log_write_start(ctx, &ctx_data, log_level)) < DLT_RETURN_OK) + { + fprintf(stderr, "dlt_procfs_log_interrupts(): dlt_user_log_write_start() returned error\n"); + return ret; + } + + if((ret = dlt_user_log_write_string(&ctx_data, "IRQ")) < DLT_RETURN_OK) + { + fprintf(stderr, "dlt_procfs_log_interrupts(): dlt_user_log_write_string() returned error\n"); + return ret; + } + + token = strtok(buffer, "\n"); + while(token != NULL) + { + if(dlt_user_log_write_string(&ctx_data, token) < DLT_RETURN_OK) + { + /* message buffer full, start new one */ + if((ret = dlt_user_log_write_finish(&ctx_data)) < DLT_RETURN_OK) + { + fprintf(stderr, "dlt_procfs_log_interrupts(): dlt_user_log_write_finish() returned error\n"); + return ret; + } + + if((ret = dlt_user_log_write_start(ctx, &ctx_data, log_level)) < DLT_RETURN_OK) + { + fprintf(stderr, "dlt_procfs_log_interrupts(): dlt_user_log_write_start() returned error\n"); + return ret; + } + + if((ret = dlt_user_log_write_string(&ctx_data, "IRQ")) < DLT_RETURN_OK) + { + fprintf(stderr, "dlt_procfs_log_interrupts(): dlt_user_log_write_string() returned error\n"); + return ret; + } + } + else + token = strtok(NULL, "\n"); + } + + if((ret = dlt_user_log_write_finish(&ctx_data)) < DLT_RETURN_OK) + { + fprintf(stderr, "dlt_procfs_log_interrupts(): dlt_user_log_write_finish() returned error\n"); + return ret; + } + + return DLT_RETURN_OK; +} diff --git a/src/procfs/dlt-procfs-interrupt.h b/src/procfs/dlt-procfs-interrupt.h new file mode 100644 index 0000000..48a3a6d --- /dev/null +++ b/src/procfs/dlt-procfs-interrupt.h @@ -0,0 +1,35 @@ +/* + * @licence app begin@ + * SPDX license identifier: MPL-2.0 + * + * Copyright (C) 2011-2015, BMW AG + * + * This file is part of GENIVI Project DLT - Diagnostic Log and Trace. + * + * This Source Code Form is subject to the terms of the + * Mozilla Public License (MPL), v. 2.0. + * If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + * + * For further information see http://www.genivi.org/. + * @licence end@ + */ + +/*! + * \author Sven Hassler + * + * \copyright Copyright © 2011-2015 BMW AG. \n + * License MPL-2.0: Mozilla Public License version 2.0 http://mozilla.org/MPL/2.0/. + * + * \file dlt-procfs-interrupt.h + */ + +#ifndef SRC_PROCFS_DLT_PROCFS_INTERRUPT_H_ +#define SRC_PROCFS_DLT_PROCFS_INTERRUPT_H_ + +#include "dlt.h" +#include "dlt-procfs-common.h" + +DltReturnValue dlt_procfs_log_interrupts(DltContext *ctx, DltLogLevelType log_level); + +#endif /* SRC_PROCFS_DLT_PROCFS_INTERRUPT_H_ */ diff --git a/src/procfs/dlt-procfs-options.c b/src/procfs/dlt-procfs-options.c new file mode 100644 index 0000000..d2aa3af --- /dev/null +++ b/src/procfs/dlt-procfs-options.c @@ -0,0 +1,259 @@ +/* + * @licence app begin@ + * SPDX license identifier: MPL-2.0 + * + * Copyright (C) 2011-2015, BMW AG + * + * This file is part of GENIVI Project DLT - Diagnostic Log and Trace. + * + * This Source Code Form is subject to the terms of the + * Mozilla Public License (MPL), v. 2.0. + * If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + * + * For further information see http://www.genivi.org/. + * @licence end@ + */ + +/*! + * \author Sven Hassler + * + * \copyright Copyright © 2011-2015 BMW AG. \n + * License MPL-2.0: Mozilla Public License version 2.0 http://mozilla.org/MPL/2.0/. + * + * \file dlt-procfs-options.c + */ + +#include "dlt-procfs.h" + +/** + * Print information how to use this program. + */ +void usage(char *prog_name) +{ + char version[255]; + dlt_get_version(version,255); + + printf("Usage: %s [options]\n", prog_name); + printf("Application to forward information from the /proc/ file system to DLT.\n"); + printf("%s\n", version); + printf("Options:\n"); + //printf(" -d Daemonize. Detach from terminal and run in background.\n"); + printf(" -c filename Use configuration file. \n"); + printf(" Default: %s\n", DEFAULT_CONF_FILE); + printf(" -h This help message.\n"); +} + +/** + * Initialize command line options with default values. + */ +void dlt_procfs_init_cli_options(DltProcfsOptions *options) +{ + options->configurationFileName = DEFAULT_CONF_FILE; + options->customConfigFile = 0; +} + +void dlt_procfs_free_cli_options(DltProcfsOptions *options) +{ + if(options->customConfigFile) + free(options->configurationFileName); +} + +DltReturnValue dlt_procfs_read_command_line(DltProcfsOptions *options, int argc, char **argv) +{ + if(options == NULL) + { + fprintf(stderr, "dlt_procfs_read_command_line(): Nullpointer parameter\n"); + return DLT_RETURN_WRONG_PARAMETER; + } + dlt_procfs_init_cli_options(options); + int opt; + + while((opt = getopt(argc, argv, "c:h")) != -1) + { + switch(opt) { + case 'c': + { + if((options->configurationFileName = malloc(strlen(optarg)+1)) == 0) + { + fprintf(stderr, "Out of memory!\n"); + return DLT_RETURN_ERROR; + } + strcpy(options->configurationFileName, optarg); /* strcpy unritical here, because size matches exactly the size to be copied */ + options->customConfigFile = 1; + break; + } + case 'h': + { + usage(argv[0]); + exit(0); + return -1;//for parasoft + } + default: + { + fprintf(stderr, "Unknown option: %c\n", optopt); + usage(argv[0]); + return DLT_RETURN_ERROR; + } + } + } + + return DLT_RETURN_OK; +} + +/** + * Initialize configuration to default values. + */ +void dlt_procfs_init_configuration(DltProcfsConfig *config) +{ + config->process_log_interval = 1000; + config->irq_log_interval = 1000; + config->log_level = DLT_LOG_DEFAULT; +} + +/** + * Read options from the configuration file + */ +DltReturnValue dlt_procfs_read_configuration_file(DltProcfsConfig *config, char *file_name) +{ + FILE *file; + char *line, *token, *value, *pch, *strchk; + int tmp; + + if(config == NULL || file_name == NULL) + { + fprintf(stderr, "Nullpointer parameter!\n"); + return DLT_RETURN_WRONG_PARAMETER; + } + + dlt_procfs_init_configuration(config); + + file = fopen(file_name, "r"); + + if(file == NULL) + { + fprintf(stderr, "Could not open configuration file!\n"); + return DLT_RETURN_ERROR; + } + + if((line = malloc(COMMAND_LINE_SIZE)) == 0) + { + fprintf(stderr, "Out of memory!\n"); + return DLT_RETURN_ERROR; + } + + if((token = malloc(COMMAND_LINE_SIZE)) == 0) + { + fprintf(stderr, "Out of memory!\n"); + return DLT_RETURN_ERROR; + } + + if((value = malloc(COMMAND_LINE_SIZE)) == 0) + { + fprintf(stderr, "Out of memory!\n"); + return DLT_RETURN_ERROR; + } + + while(fgets(line, COMMAND_LINE_SIZE, file) != NULL) + { + token[0] = '\0'; + value[0] = '\0'; + + pch = strtok (line, " =\r\n"); + while(pch != NULL) + { + if(pch[0] == '#') + break; + + if(token[0] == '\0') + { + strncpy(token, pch, COMMAND_LINE_SIZE-1); + token[COMMAND_LINE_SIZE-1] = '\0'; + } + else + { + strncpy(value, pch, COMMAND_LINE_SIZE-1); + value[COMMAND_LINE_SIZE-1] = '\0'; + break; + } + + pch = strtok(NULL, " =\r\n"); + } + + if(token[0] != '\0' && value[0] != '\0') + { + if(strcmp(token, "process_interval") == '\0') + { + tmp = strtol(value, &strchk, 10); + + if(strchk[0] == '\0' && tmp > 0) + config->process_log_interval = tmp; + else + fprintf(stderr, "Error reading configuration file: %s is not a valid value for %s\n", value, token); + } + else if(strcmp(token, "irq_interval") == '\0') + { + tmp = strtol(value, &strchk, 10); + + if(strchk[0] == '\0' && tmp > 0) + config->irq_log_interval = tmp; + else + fprintf(stderr, "Error reading configuration file: %s is not a valid value for %s\n", value, token); + } + else if(strcmp(token, "check_interval") == '\0') + { + tmp = strtol(value, &strchk, 10); + + if(strchk[0] == '\0' && tmp > 0) + config->check_log_interval = tmp; + else + fprintf(stderr, "Error reading configuration file: %s is not a valid value for %s\n", value, token); + } + else if(strcmp(token, "log_level") == '\0') + { + tmp = strtol(value, &strchk, 10); + + if(strchk[0] == '\0' && tmp >= -1 && tmp <= 6) + config->log_level = tmp; + else + fprintf(stderr, "Error reading configuration file: %s is not a valid value for %s\n", value, token); + } + } + } + + fclose(file); + free(value); + free(token); + free(line); + + return DLT_RETURN_OK; +} + +DltReturnValue dlt_procfs_init(int argc, char **argv, DltProcfsConfig *config) +{ + DltProcfsOptions options; + + DltReturnValue ret; + + if(config == NULL) + { + fprintf(stderr, "Nullpointer parameter!"); + return DLT_RETURN_WRONG_PARAMETER; + } + + if((ret = dlt_procfs_read_command_line(&options, argc, argv)) < DLT_RETURN_OK) + { + fprintf(stderr, "Failed to read command line!"); + return ret; + } + + if((ret = dlt_procfs_read_configuration_file(config, options.configurationFileName)) < DLT_RETURN_OK) + { + fprintf(stderr, "Failed to read configuration file!"); + return ret; + } + + dlt_procfs_free_cli_options(&options); + + return DLT_RETURN_OK; +} diff --git a/src/procfs/dlt-procfs-process-list.c b/src/procfs/dlt-procfs-process-list.c new file mode 100644 index 0000000..9c98bdc --- /dev/null +++ b/src/procfs/dlt-procfs-process-list.c @@ -0,0 +1,288 @@ +/* + * @licence app begin@ + * SPDX license identifier: MPL-2.0 + * + * Copyright (C) 2011-2015, BMW AG + * + * This file is part of GENIVI Project DLT - Diagnostic Log and Trace. + * + * This Source Code Form is subject to the terms of the + * Mozilla Public License (MPL), v. 2.0. + * If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + * + * For further information see http://www.genivi.org/. + * @licence end@ + */ + +/*! + * \author Sven Hassler + * + * \copyright Copyright © 2011-2015 BMW AG. \n + * License MPL-2.0: Mozilla Public License version 2.0 http://mozilla.org/MPL/2.0/. + * + * \file dlt-procfs-process-list.c + */ + +#include "dlt-procfs-process-list.h" + +DltProcfsProcessList *dlt_procfs_create_process_list() +{ + DltProcfsProcessList *new_list = malloc(sizeof(DltProcfsProcessList)); + if(new_list == NULL) + { + fprintf(stderr, "Cannot create process list, out of memory\n"); + return NULL; + } + + memset(new_list, 0, sizeof(DltProcfsProcessList)); + new_list->start = new_list->cursor = NULL; + + return new_list; +} + +DltReturnValue dlt_procfs_free_process_list_soft(DltProcfsProcessList *list) +{ + if(list == NULL) + { + fprintf(stderr, "dlt_procfs_free_process_list_soft: Nullpointer parameter\n"); + return DLT_RETURN_WRONG_PARAMETER; + } + + free(list); + + return DLT_RETURN_OK; +} + +DltReturnValue dlt_procfs_free_process_list(DltProcfsProcessList *list) +{ + if(list == NULL) + { + fprintf(stderr, "dlt_procfs_free_process_list: Nullpointer parameter\n"); + return DLT_RETURN_WRONG_PARAMETER; + } + + DltProcfsProcess *tmp; + + list->cursor = list->start; + while(list->cursor != NULL) + { + tmp = list->cursor->next; + dlt_procfs_free_process(list->cursor); + list->cursor = tmp; + } + + return dlt_procfs_free_process_list_soft(list); +} + +DltProcfsProcess *dlt_procfs_get_process_at_cursor(DltProcfsProcessList *list) +{ + if(list == NULL) + { + fprintf(stderr, "dlt_procfs_get_process_at_cursor(): Nullpointer parameter\n"); + return NULL; + } + + return list->cursor; +} + +DltReturnValue dlt_procfs_reset_cursor(DltProcfsProcessList *list) +{ + if(list == NULL) + { + fprintf(stderr, "dlt_procfs_reset_cursor(): Nullpointer parameter\n"); + return DLT_RETURN_WRONG_PARAMETER; + } + + list->cursor = list->start; + return DLT_RETURN_OK; +} + +DltReturnValue dlt_procfs_set_cursor_at_end(DltProcfsProcessList *list) +{ + if(list == NULL) + { + fprintf(stderr, "dlt_procfs_set_cursor_at_end(): Nullpointer parameter\n"); + return DLT_RETURN_WRONG_PARAMETER; + } + + list->cursor = list->start; + if(list->cursor == NULL) + return DLT_RETURN_OK; + + while(list->cursor->next != NULL) + dlt_procfs_increment_cursor(list); + + return DLT_RETURN_OK; +} + +DltReturnValue dlt_procfs_increment_cursor(DltProcfsProcessList *list) +{ + if(list == NULL) + { + fprintf(stderr, "dlt_procfs_set_cursor_at_end(): Nullpointer parameter\n"); + return DLT_RETURN_WRONG_PARAMETER; + } + + if(list->cursor == NULL) + return DLT_RETURN_ERROR; + + list->cursor = list->cursor->next; + + return DLT_RETURN_OK; +} + +DltReturnValue dlt_procfs_decrement_cursor(DltProcfsProcessList *list) +{ + if(list == NULL) + { + fprintf(stderr, "dlt_procfs_set_cursor_at_end(): Nullpointer parameter\n"); + return DLT_RETURN_WRONG_PARAMETER; + } + + if(list->cursor == NULL) + return DLT_RETURN_ERROR; + + list->cursor = list->cursor->prev; + + return DLT_RETURN_OK; +} + +DltReturnValue dlt_procfs_add_process_at_start(DltProcfsProcessList *list, DltProcfsProcess *process) +{ + if(list == NULL || process == NULL) + { + fprintf(stderr, "dlt_procfs_add_process_at_start(): Nullpointer parameter\n"); + return DLT_RETURN_WRONG_PARAMETER; + } + + if(list->start != NULL) + list->start->prev = process; + + process->next = list->start; + list->start = process; + + return DLT_RETURN_OK; +} + +DltReturnValue dlt_procfs_add_process_before_cursor(DltProcfsProcessList *list, DltProcfsProcess *process) +{ + if(list == NULL || process == NULL) + { + fprintf(stderr, "dlt_procfs_add_process_before_cursor(): Nullpointer parameter\n"); + return DLT_RETURN_WRONG_PARAMETER; + } + + if(list->start == NULL) // Empty list? + { + DltReturnValue ret = dlt_procfs_add_process_at_start(list, process); + list->cursor = NULL; + return ret; + } + else if(list->cursor == NULL) + { + dlt_procfs_set_cursor_at_end(list); + DltReturnValue ret = dlt_procfs_add_process_after_cursor(list, process); + list->cursor = NULL; + return ret; + } + + if(list->cursor->prev != NULL) + list->cursor->prev->next = process; + else + list->start = process; + + process->next = list->cursor; + process->prev = list->cursor->prev; + list->cursor->prev = process; + + return DLT_RETURN_OK; +} + +DltReturnValue dlt_procfs_add_process_after_cursor(DltProcfsProcessList *list, DltProcfsProcess *process) +{ + if(list == NULL || process == NULL) + { + fprintf(stderr, "dlt_procfs_add_process_after_cursor: Nullpointer parameter\n"); + return DLT_RETURN_ERROR; + } + + if(list->cursor == NULL) + return dlt_procfs_add_process_at_start(list, process); + + if(list->cursor->next != NULL) + list->cursor->next->prev = process; + + process->next = list->cursor->next; + process->prev = list->cursor; + list->cursor->next = process; + + return DLT_RETURN_OK; +} + +DltReturnValue dlt_procfs_remove_process_at_cursor_soft(DltProcfsProcessList *list) +{ + if(list == NULL) + { + fprintf(stderr, "dlt_procfs_set_cursor_at_end(): Nullpointer parameter\n"); + return DLT_RETURN_WRONG_PARAMETER; + } + + if(list->cursor == NULL) + { + fprintf(stderr, "Could not remove process from list - cursor is NULL!\n"); + return DLT_RETURN_ERROR; + } + + DltProcfsProcess *tmp = list->cursor; + + if(tmp->prev != NULL) + { + if(tmp->next != NULL) + { + tmp->prev->next = tmp->next; + tmp->next->prev = tmp->prev; + } + else + tmp->prev->next = NULL; + } + else + { + if(tmp->next != NULL) + { + tmp->next->prev = NULL; + list->start = tmp->next; + } + else + list->start = NULL; + } + + list->cursor = tmp->next; // becomes NULL if list is at end + + return DLT_RETURN_OK; +} + +DltReturnValue dlt_procfs_remove_process_at_cursor(DltProcfsProcessList *list) +{ + if(list == NULL) + { + fprintf(stderr, "dlt_procfs_set_cursor_at_end(): Nullpointer parameter\n"); + return DLT_RETURN_WRONG_PARAMETER; + } + + if(list->cursor == NULL) + { + fprintf(stderr, "Could not remove process from list - cursor is NULL!\n"); + return DLT_RETURN_ERROR; + } + + DltProcfsProcess *tmp = list->cursor; + DltReturnValue ret = dlt_procfs_remove_process_at_cursor_soft(list); + if(ret < DLT_RETURN_OK) + return ret; + + dlt_procfs_free_process(tmp); + + return DLT_RETURN_OK; +} + diff --git a/src/procfs/dlt-procfs-process-list.h b/src/procfs/dlt-procfs-process-list.h new file mode 100644 index 0000000..1545ab3 --- /dev/null +++ b/src/procfs/dlt-procfs-process-list.h @@ -0,0 +1,54 @@ +/* + * @licence app begin@ + * SPDX license identifier: MPL-2.0 + * + * Copyright (C) 2011-2015, BMW AG + * + * This file is part of GENIVI Project DLT - Diagnostic Log and Trace. + * + * This Source Code Form is subject to the terms of the + * Mozilla Public License (MPL), v. 2.0. + * If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + * + * For further information see http://www.genivi.org/. + * @licence end@ + */ + +/*! + * \author Sven Hassler + * + * \copyright Copyright © 2011-2015 BMW AG. \n + * License MPL-2.0: Mozilla Public License version 2.0 http://mozilla.org/MPL/2.0/. + * + * \file dlt-procfs-process-list.h + */ + +#ifndef SRC_PROCFS_DLT_PROCFS_PROCESS_LIST_H_ +#define SRC_PROCFS_DLT_PROCFS_PROCESS_LIST_H_ + +#include "dlt-procfs-common.h" +#include "dlt-procfs-process.h" + +typedef struct +{ + struct DltProcfsProcess *start, *cursor; +} DltProcfsProcessList; + +DltProcfsProcessList *dlt_procfs_create_process_list(); +DltReturnValue dlt_procfs_free_process_list_soft(DltProcfsProcessList *list); +DltReturnValue dlt_procfs_free_process_list(DltProcfsProcessList *list); +DltProcfsProcess *dlt_procfs_get_process_at_cursor(DltProcfsProcessList *list); +DltReturnValue dlt_procfs_increment_cursor(DltProcfsProcessList *list); +DltReturnValue dlt_procfs_decrement_cursor(DltProcfsProcessList *list); +DltReturnValue dlt_procfs_reset_cursor(DltProcfsProcessList *list); +DltReturnValue dlt_procfs_add_process_at_start(DltProcfsProcessList *list, DltProcfsProcess *process); +DltReturnValue dlt_procfs_add_process_before_cursor(DltProcfsProcessList *list, DltProcfsProcess *process); +DltReturnValue dlt_procfs_add_process_after_cursor(DltProcfsProcessList *list, DltProcfsProcess *process); +DltReturnValue dlt_procfs_remove_process_at_cursor_soft(DltProcfsProcessList *list); +DltReturnValue dlt_procfs_remove_process_at_cursor(DltProcfsProcessList *list); + +// DltReturnValue dlt_procfs_remove_process_after_cursor(DltProcfsProcessList *list); +// DltReturnValue dlt_procfs_remove_first_process(DltProcfsProcessList *list); + +#endif /* SRC_PROCFS_DLT_PROCFS_PROCESS_LIST_H_ */ diff --git a/src/procfs/dlt-procfs-process.c b/src/procfs/dlt-procfs-process.c new file mode 100644 index 0000000..7bb815f --- /dev/null +++ b/src/procfs/dlt-procfs-process.c @@ -0,0 +1,467 @@ +/* + * @licence app begin@ + * SPDX license identifier: MPL-2.0 + * + * Copyright (C) 2011-2015, BMW AG + * + * This file is part of GENIVI Project DLT - Diagnostic Log and Trace. + * + * This Source Code Form is subject to the terms of the + * Mozilla Public License (MPL), v. 2.0. + * If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + * + * For further information see http://www.genivi.org/. + * @licence end@ + */ + +/*! + * \author Sven Hassler + * + * \copyright Copyright © 2011-2015 BMW AG. \n + * License MPL-2.0: Mozilla Public License version 2.0 http://mozilla.org/MPL/2.0/. + * + * \file dlt-procfs-process.c + */ + +#include "dlt-procfs-process.h" +#include +#include + +DltReturnValue dlt_procfs_read_process_file_to_str(pid_t pid, char **target_str, char *subdir); +unsigned long int dlt_procfs_read_process_stat_to_ulong(pid_t pid, unsigned int index); +DltReturnValue dlt_procfs_read_process_stat_cmdline(pid_t pid, char **buffer); + +DltReturnValue dlt_procfs_process_update_io_wait(DltProcfsProcess *process, unsigned long int time_dif_ms) +{ + if(process == NULL) + { + fprintf(stderr, "dlt_procfs_process_update_io_wait(): Nullpointer parameter\n"); + return DLT_RETURN_WRONG_PARAMETER; + } + + unsigned long int total_io_wait = dlt_procfs_read_process_stat_to_ulong(process->pid, 42); + + process->io_wait = (total_io_wait - process->last_io_wait) * 1000 / sysconf(_SC_CLK_TCK); // busy milliseconds since last update + if(time_dif_ms > 0) + process->io_wait = process->io_wait * 1000 / time_dif_ms; // busy milliseconds per second + + process->last_io_wait = total_io_wait; + + return DLT_RETURN_OK; +} + +DltReturnValue dlt_procfs_process_update_cpu_time(DltProcfsProcess *process, unsigned long int time_dif_ms) +{ + if(process == NULL) + { + fprintf(stderr, "dlt_procfs_process_update_cpu_time(): Nullpointer parameter\n"); + return DLT_RETURN_WRONG_PARAMETER; + } + + unsigned long int utime = dlt_procfs_read_process_stat_to_ulong(process->pid, 14); + unsigned long int stime = dlt_procfs_read_process_stat_to_ulong(process->pid, 15); + + unsigned long total_cpu_time = utime + stime; + + process->cpu_time = (total_cpu_time - process->last_cpu_time) * 1000 / sysconf(_SC_CLK_TCK); // busy milliseconds since last update + if(time_dif_ms > 0) + process->cpu_time = process->cpu_time * 1000 / time_dif_ms; // busy milliseconds per second + + process->last_cpu_time = total_cpu_time; + + return DLT_RETURN_OK; +} + +DltReturnValue dlt_procfs_process_update_rss(DltProcfsProcess *process) +{ + if(process == NULL) + { + fprintf(stderr, "dlt_procfs_process_update_rss(): Nullpointer parameter\n"); + return DLT_RETURN_WRONG_PARAMETER; + } + + process->rss = dlt_procfs_read_process_stat_to_ulong(process->pid, 24); + + return DLT_RETURN_OK; +} + +DltReturnValue dlt_procfs_process_update_ctx_switches(DltProcfsProcess *process) +{ + if(process == NULL) + { + fprintf(stderr, "dlt_procfs_process_update_ctx_switches(): Nullpointer parameter\n"); + return DLT_RETURN_WRONG_PARAMETER; + } + + char *buffer, *tok, *last_tok; + char *delim = " :\t\n"; + last_tok = NULL; + + DltReturnValue ret; + if((ret = dlt_procfs_read_process_file_to_str(process->pid, &buffer, "status")) < DLT_RETURN_OK) return ret; + + process->ctx_switches = 0; + + tok = strtok(buffer, delim); + while(tok != NULL) + { + if(last_tok != NULL) + { + if(strcmp(last_tok, "voluntary_ctxt_switches") == 0 || strcmp(last_tok, "nonvoluntary_ctxt_switches") == 0) + { + char *chk; + process->ctx_switches += strtol(tok, &chk, 10); + + if(*chk != '\0') + { + fprintf(stderr, "Could not parse ctx_switches info from /proc/%d/status", process->pid); + free(buffer); + return DLT_RETURN_ERROR; + } + } + } + + last_tok = tok; + tok = strtok(NULL, delim); + } + + free(buffer); + + return DLT_RETURN_OK; +} + +DltReturnValue dlt_procfs_process_update_io_bytes(DltProcfsProcess *process) +{ + if(process == NULL) + { + fprintf(stderr, "dlt_procfs_process_update_io_bytes: Nullpointer parameter\n"); + return DLT_RETURN_WRONG_PARAMETER; + } + + char *buffer, *tok, *last_tok; + char *delim = " :\t\n"; + last_tok = NULL; + + DltReturnValue ret; + if((ret = dlt_procfs_read_process_file_to_str(process->pid, &buffer, "io")) < DLT_RETURN_OK) + return ret; + + process->io_bytes = 0; + + tok = strtok(buffer, delim); + while(tok != NULL) + { + if(last_tok != NULL) + { + if(strcmp(last_tok, "rchar") == 0 || strcmp(last_tok, "wchar") == 0) + { + char *chk; + process->io_bytes += strtoul(tok, &chk, 10); + + if(*chk != '\0') + { + fprintf(stderr, "Could not parse io_bytes info from /proc/%d/io", process->pid); + free(buffer); + return DLT_RETURN_ERROR; + } + } + } + + last_tok = tok; + tok = strtok(NULL, delim); + } + + free(buffer); + + return DLT_RETURN_OK; +} + +DltReturnValue dlt_procfs_update_process(DltProcfsProcess *process, unsigned long int time_dif_ms) +{ + dlt_procfs_process_update_io_wait(process, time_dif_ms); + dlt_procfs_process_update_cpu_time(process, time_dif_ms); + dlt_procfs_process_update_rss(process); + dlt_procfs_process_update_ctx_switches(process); + dlt_procfs_process_update_io_bytes(process); + + return DLT_RETURN_OK; +} + +DltProcfsProcess *dlt_procfs_create_process(int pid) +{ + DltProcfsProcess *new_process = malloc(sizeof(DltProcfsProcess)); + memset(new_process, 0, sizeof(DltProcfsProcess)); + + new_process->pid = pid; + new_process->ppid = (pid_t)dlt_procfs_read_process_stat_to_ulong(pid, 4); + + dlt_procfs_read_process_file_to_str(pid, &(new_process->command_line), "cmdline"); + if(new_process->command_line != NULL) + if(strlen(new_process->command_line) == 0) + { + free(new_process->command_line); + dlt_procfs_read_process_stat_cmdline(pid, &(new_process->command_line)); + } + + dlt_procfs_update_process(new_process, 0); + + return new_process; +} + +DltProcfsProcess *dlt_procfs_clone_process(DltProcfsProcess *original) +{ + if(original == NULL) + { + fprintf(stderr, "dlt_procfs_clone_process: Nullpointer parameter\n"); + return NULL; + } + + // DltProcfsProcess *new_process = dlt_procfs_create_process(original->pid); + DltProcfsProcess *new_process = malloc(sizeof(DltProcfsProcess)); + if(new_process == NULL) + { + fprintf(stderr, "Out of memory\n"); + return NULL; + } + + memcpy(new_process, original, sizeof(DltProcfsProcess)); + + if(original->command_line != NULL) + { + new_process->command_line = malloc(strlen(original->command_line) + 1); + if(new_process->command_line == NULL) + { + fprintf(stderr, "Out of memory\n"); + return NULL; + } + strncpy(new_process->command_line, original->command_line, strlen(original->command_line) + 1); + } + else + new_process->command_line = NULL; + + new_process->next = new_process->prev = NULL; + + return new_process; +} + +DltReturnValue dlt_procfs_free_process(DltProcfsProcess *process) +{ + if(process == NULL) + { + fprintf(stderr, "dlt_procfs_free_process: Nullpointer parameter\n"); + return DLT_RETURN_WRONG_PARAMETER; + } + + if(process->command_line != NULL) + free(process->command_line); + + free(process); + + return DLT_RETURN_OK; +} + +DltReturnValue dlt_procfs_print_process(DltProcfsProcess *process) +{ + if(process == NULL) + { + fprintf(stderr, "Error: Nullpointer parameter\n"); + return DLT_RETURN_ERROR; + } + + printf("[PID %d]\n", process->pid); + printf(" > PPID : %d\n", process->ppid); + printf(" > CMDLINE : %s\n", process->command_line); + printf(" > CPUTIME : %lu (busy ms/s)\n", process->cpu_time); + printf(" > RSS : %ld\n", process->rss); + printf(" > CTXSWTC : %ld\n", process->ctx_switches); + printf(" > IOBYTES : %lu\n", process->io_bytes); + printf(" > IOWAIT : %ld (%ld)\n", process->io_wait, process->last_io_wait); + + return DLT_RETURN_OK; +} + +DltReturnValue dlt_procfs_read_process_file_to_str(pid_t pid, char **target_str, char *subdir) +{ + if(target_str == NULL) + { + fprintf(stderr, "Error: Nullpointer parameter\n"); + return DLT_RETURN_ERROR; + } + + *target_str = NULL; + + if(pid <= 0) + { + fprintf(stderr, "Error: Invalid PID\n"); + return DLT_RETURN_ERROR; + } + + if(subdir == NULL) + { + fprintf(stderr, "Error: Nullpointer parameter\n"); + return DLT_RETURN_ERROR; + } + + char filename[BUFFER_SIZE]; + snprintf(filename, BUFFER_SIZE, "/proc/%d/%s", pid, subdir); + + return dlt_procfs_read_file_compact(filename, target_str); +} + +unsigned long int dlt_procfs_read_process_stat_to_ulong(pid_t pid, unsigned int index) +{ + if(pid <= 0) + { + fprintf(stderr, "dlt_procfs_read_process_stat_to_ulong(): Invalid PID\n"); + return 0; + } + + char *buffer = NULL; + DltReturnValue tmp = dlt_procfs_read_process_file_to_str(pid, &buffer, "stat"); + if(tmp < DLT_RETURN_OK) + { + if(buffer != NULL) + free(buffer); + + return tmp; + } + + char *tok = strtok(buffer, " \t\n"); + unsigned int i = 1, found = 0; + + while(tok != NULL) + { + if(i == index) + { + found = 1; + break; + } + i++; + tok = strtok(NULL, " \t\n"); + } + + unsigned long int ret = 0; + + if(found) + { + char *check = NULL; + ret = strtoul(tok, &check, 10); + if(*check != '\0') + { + fprintf(stderr, "dlt_procfs_read_process_stat_to_ulong(): Could not extract token\n"); + ret = 0; + } + } + else + fprintf(stderr, "dlt_procfs_read_process_stat_to_ulong(): Index not found\n"); + + free(buffer); + + return ret; +} + +DltReturnValue dlt_procfs_read_process_stat_cmdline(pid_t pid, char **buffer) +{ + if(pid <= 0) + { + fprintf(stderr, "dlt_procfs_read_process_stat_cmdline(): Invalid PID\n"); + return DLT_RETURN_WRONG_PARAMETER; + } + + if(buffer == NULL) + { + fprintf(stderr, "dlt_procfs_read_process_stat_cmdline(): Nullpointer parameter\n"); + return DLT_RETURN_WRONG_PARAMETER; + } + + char *tmp_buffer = NULL; + DltReturnValue tmp = dlt_procfs_read_process_file_to_str(pid, &tmp_buffer, "stat"); + if(tmp < DLT_RETURN_OK) + { + if(tmp_buffer != NULL) + free(tmp_buffer); + + return tmp; + } + + char *tok = strtok(tmp_buffer, " \t\n"); + unsigned int i = 1; + + while(tok != NULL) + { + if(i == 2) + { + break; + } + i++; + tok = strtok(NULL, " \t\n"); + } + + if(i == 2) + { + (*buffer) = malloc(strlen(tok) + 1); + strncpy(*buffer, tok, strlen(tok) + 1); + } + else + { + fprintf(stderr, "dlt_procfs_read_process_stat_cmdline(): cmdline entry not found\n"); + return DLT_RETURN_ERROR; + } + + free(tmp_buffer); + + return DLT_RETURN_OK; +} + +DltReturnValue dlt_procfs_get_msg_process_update(DltProcfsProcess *process, char *buffer, int maxlen) +{ + if(process == NULL || buffer == NULL) + { + fprintf(stderr, "dlt_procfs_log_process_new(): Nullpointer parameter\n"); + return DLT_RETURN_WRONG_PARAMETER; + } + + snprintf(buffer, maxlen, "%d;%lu;%ld;%ld;%lu;%lu", process->pid, process->cpu_time, process->rss, process->ctx_switches, process->io_bytes, process->io_wait); + + return DLT_RETURN_OK; +} + +DltReturnValue dlt_procfs_get_msg_process_new(DltProcfsProcess *process, char *buffer, int maxlen) +{ + if(process == NULL || buffer == NULL) + { + fprintf(stderr, "dlt_procfs_log_process_new(): Nullpointer parameter\n"); + return DLT_RETURN_WRONG_PARAMETER; + } + + snprintf(buffer, maxlen, "%d;%d;%s", process->pid, process->ppid, process->command_line); + + return DLT_RETURN_OK; +} + +DltReturnValue dlt_procfs_get_msg_process_stop(DltProcfsProcess *process, char *buffer, int maxlen) +{ + if(process == NULL || buffer == NULL) + { + fprintf(stderr, "dlt_procfs_log_process_new(): Nullpointer parameter\n"); + return DLT_RETURN_WRONG_PARAMETER; + } + + snprintf(buffer, maxlen, "%d", process->pid); + + return DLT_RETURN_OK; +} + +DltReturnValue dlt_procfs_get_msg_process_commandline(DltProcfsProcess *process, char *buffer, int maxlen) +{ + if(process == NULL || buffer == NULL) + { + fprintf(stderr, "dlt_procfs_log_process_new(): Nullpointer parameter\n"); + return DLT_RETURN_WRONG_PARAMETER; + } + + snprintf(buffer, maxlen, "%d;%s", process->pid, process->command_line); + + return DLT_RETURN_OK; +} diff --git a/src/procfs/dlt-procfs-process.h b/src/procfs/dlt-procfs-process.h new file mode 100644 index 0000000..92c32db --- /dev/null +++ b/src/procfs/dlt-procfs-process.h @@ -0,0 +1,56 @@ +/* + * @licence app begin@ + * SPDX license identifier: MPL-2.0 + * + * Copyright (C) 2011-2015, BMW AG + * + * This file is part of GENIVI Project DLT - Diagnostic Log and Trace. + * + * This Source Code Form is subject to the terms of the + * Mozilla Public License (MPL), v. 2.0. + * If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + * + * For further information see http://www.genivi.org/. + * @licence end@ + */ + +/*! + * \author Sven Hassler + * + * \copyright Copyright © 2011-2015 BMW AG. \n + * License MPL-2.0: Mozilla Public License version 2.0 http://mozilla.org/MPL/2.0/. + * + * \file dlt-procfs-process.h + */ + +#ifndef SRC_PROCFS_DLT_PROCFS_PROCESS_H_ +#define SRC_PROCFS_DLT_PROCFS_PROCESS_H_ + +#include "dlt.h" +#include "dlt-procfs-common.h" +#include + +typedef struct DltProcfsEventWatch DltProcfsEventWatch; // forward declaration + +typedef struct DltProcfsProcess +{ + pid_t pid, ppid; + char *command_line; + unsigned long int cpu_time, last_cpu_time, io_wait, last_io_wait, io_bytes; + long int rss, ctx_switches; + + struct DltProcfsProcess *next, *prev; +} DltProcfsProcess; + +DltProcfsProcess *dlt_procfs_create_process(); +DltProcfsProcess *dlt_procfs_clone_process(DltProcfsProcess *original); +DltReturnValue dlt_procfs_free_process(DltProcfsProcess *process); +DltReturnValue dlt_procfs_print_process(DltProcfsProcess *process); +DltReturnValue dlt_procfs_update_process(DltProcfsProcess *process, unsigned long int time_dif_ms); +DltReturnValue dlt_procfs_get_msg_process_new(DltProcfsProcess *process, char *buffer, int maxlen); +DltReturnValue dlt_procfs_get_msg_process_stop(DltProcfsProcess *process, char *buffer, int maxlen); +DltReturnValue dlt_procfs_get_msg_process_update(DltProcfsProcess *process, char *buffer, int maxlen); +DltReturnValue dlt_procfs_get_msg_process_commandline(DltProcfsProcess *process, char *buffer, int maxlen); + +#endif /* SRC_PROCFS_DLT_PROCFS_PROCESS_H_ */ diff --git a/src/procfs/dlt-procfs.c b/src/procfs/dlt-procfs.c new file mode 100644 index 0000000..4396c27 --- /dev/null +++ b/src/procfs/dlt-procfs.c @@ -0,0 +1,503 @@ +/* + * @licence app begin@ + * SPDX license identifier: MPL-2.0 + * + * Copyright (C) 2011-2015, BMW AG + * + * This file is part of GENIVI Project DLT - Diagnostic Log and Trace. + * + * This Source Code Form is subject to the terms of the + * Mozilla Public License (MPL), v. 2.0. + * If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + * + * For further information see http://www.genivi.org/. + * @licence end@ + */ + +/*! + * \author Sven Hassler + * + * \copyright Copyright © 2011-2015 BMW AG. \n + * License MPL-2.0: Mozilla Public License version 2.0 http://mozilla.org/MPL/2.0/. + * + * \file dlt-procfs.c + */ + +#include "dlt-procfs.h" +#include +#include +#include +#include +#include + +DLT_DECLARE_CONTEXT(procfs_ctx); + +DltProcfsConfig config; + +static volatile sig_atomic_t stop_loop = 0; +static DltProcfsProcessList *list, *new_process_list, *stopped_process_list, *update_process_list; +static struct timespec _tmp_time; +static pthread_mutex_t process_list_mutex; + +void dlt_procfs_stop_loops(int sig); +void dlt_procfs_init_sigterm_handler(); +DltReturnValue dlt_procfs_init_process_lists(); +DltReturnValue dlt_procfs_free_process_lists(); +void *dlt_procfs_start_process_thread(); +DltReturnValue dlt_procfs_process_loop(); +DltReturnValue dlt_procfs_update_process_list(DltProcfsProcessList *list, unsigned long int time_dif_ms); +void *dlt_procfs_start_irq_thread(); +DltReturnValue dlt_procfs_irq_loop(); +void *dlt_procfs_start_check_thread(); +DltReturnValue dlt_procfs_check_loop(); +DltReturnValue dlt_procfs_log_check_commandlines(); + +unsigned long int timespec_to_millis(struct timespec *time) +{ + return (time->tv_sec) * 1000 + (time->tv_nsec / 1000000); +} + +unsigned long int get_millis() +{ + clock_gettime(CLOCK_REALTIME, &_tmp_time); + return timespec_to_millis(&_tmp_time); +} + +int main(int argc, char **argv) +{ + printf("Launching dlt-procfs...\n"); + + if(dlt_procfs_init(argc, argv, &config) < DLT_RETURN_OK) + { + fprintf(stderr, "Initialization error!\n"); + return -1; + } + + dlt_procfs_init_sigterm_handler(); + + if(dlt_procfs_init_process_lists() < DLT_RETURN_OK) + { + fprintf(stderr, "Error occurred initializing process lists\n"); + return -1; + } + + if(pthread_mutex_init(&process_list_mutex, NULL) < 0) + { + fprintf(stderr, "Error occurred initializing mutex\n"); + return -1; + } + + DLT_REGISTER_APP("PROC", "/proc/-filesystem logger application"); + DLT_REGISTER_CONTEXT_LL_TS(procfs_ctx, "PROC", "/proc/-filesystem logger context", config.log_level, 0); + + pthread_t process_thread; + pthread_t irq_thread; + pthread_t check_thread; + + if(pthread_create(&process_thread, NULL, &dlt_procfs_start_process_thread, NULL) != 0) + { + fprintf(stderr, "Could not create thread\n"); + return -1; + } + if(pthread_create(&irq_thread, NULL, &dlt_procfs_start_irq_thread, NULL) != 0) + { + fprintf(stderr, "Could not create thread\n"); + return -1; + } + if(pthread_create(&check_thread, NULL, &dlt_procfs_start_check_thread, NULL) != 0) + { + fprintf(stderr, "Could not create thread\n"); + return -1; + } + + pthread_join(process_thread, NULL); + pthread_join(irq_thread, NULL); + pthread_join(check_thread, NULL); + + DLT_UNREGISTER_CONTEXT(procfs_ctx); + DLT_UNREGISTER_APP(); + + pthread_mutex_destroy(&process_list_mutex); + + dlt_procfs_free_process_lists(); + + printf("Done.\n"); +} + +void dlt_procfs_init_sigterm_handler() +{ + struct sigaction action; + memset(&action, 0, sizeof(struct sigaction)); + action.sa_handler = dlt_procfs_stop_loops; + + sigaction(SIGTERM, &action, NULL); +} + +void dlt_procfs_stop_loops(int sig) +{ + if(sig > -1) + fprintf(stderr, "dlt-procfs is now terminating due to signal %d...\n", sig); + else + fprintf(stderr, "dlt-procfs is now terminating due to an error...\n"); + + stop_loop = 1; +} + +DltReturnValue dlt_procfs_init_process_lists() +{ + if((list = dlt_procfs_create_process_list()) == NULL) return DLT_RETURN_ERROR; + if((new_process_list = dlt_procfs_create_process_list()) == NULL) return DLT_RETURN_ERROR; + if((stopped_process_list = dlt_procfs_create_process_list()) == NULL) return DLT_RETURN_ERROR; + if((update_process_list = dlt_procfs_create_process_list()) == NULL) return DLT_RETURN_ERROR; + + return DLT_RETURN_OK; +} + +DltReturnValue dlt_procfs_free_process_lists() +{ + DltReturnValue ret = DLT_RETURN_OK; + + if(dlt_procfs_free_process_list(list) < DLT_RETURN_OK) + ret = DLT_RETURN_ERROR; + + if(dlt_procfs_free_process_list(new_process_list) < DLT_RETURN_OK) + ret = DLT_RETURN_ERROR; + + if(dlt_procfs_free_process_list(stopped_process_list) < DLT_RETURN_OK) + ret = DLT_RETURN_ERROR; + + if(dlt_procfs_free_process_list(update_process_list) < DLT_RETURN_OK) + ret = DLT_RETURN_ERROR; + + return ret; +} + +void *dlt_procfs_start_process_thread() +{ + if(dlt_procfs_process_loop() < DLT_RETURN_OK) + dlt_procfs_stop_loops(-1); + + return NULL; +} + +DltReturnValue dlt_procfs_process_loop() +{ + static unsigned long int old_millis, sleep_millis, dif_millis; + + old_millis = get_millis(); + + while(!stop_loop) + { + /*DltReturnValue ret = */ dlt_procfs_update_process_list(list, config.process_log_interval); + //if(ret < DLT_RETURN_OK) + // return ret; + + dif_millis = get_millis() - old_millis; + + if(dif_millis >= (unsigned long)(config.process_log_interval)) + sleep_millis = 0; + else + sleep_millis = config.process_log_interval - dif_millis; + + usleep(sleep_millis * 1000); + + old_millis = get_millis(); + } + + return DLT_RETURN_OK; +} + +DltReturnValue dlt_procfs_log_list(DltProcfsProcessList *list, DltReturnValue(*process_callback)(DltProcfsProcess*, char*, int), char *title, int delete_elements) +{ + if(list == NULL || process_callback == NULL || title == NULL) + { + fprintf(stderr, "dlt_procfs_log_list(): Nullpointer parameter\n"); + return DLT_RETURN_WRONG_PARAMETER; + } + + dlt_procfs_reset_cursor(list); + if(list->cursor == NULL) + return DLT_RETURN_OK; // list empty; nothing to do + + DltReturnValue ret; + DltContextData data; + + char buffer[BUFFER_SIZE]; + buffer[0] = '\0'; + + if((ret = dlt_user_log_write_start(&procfs_ctx, &data, config.log_level)) < DLT_RETURN_OK) + { + fprintf(stderr, "dlt_procfs_log_list(): dlt_user_log_write_start() returned error.\n"); + return ret; + } + + if((ret = dlt_user_log_write_string(&data, title)) < DLT_RETURN_OK) + { + fprintf(stderr, "dlt_procfs_log_list(): dlt_user_log_write_string() returned error.\n"); + return ret; + } + + do + { + if((ret = (*process_callback)(list->cursor, buffer, sizeof(buffer) - 1)) < DLT_RETURN_OK) + return ret; + + if((ret = dlt_user_log_write_string(&data, buffer)) < DLT_RETURN_OK) + { + /* Log buffer full => Write log and start new one*/ + if((ret = dlt_user_log_write_finish(&data)) < DLT_RETURN_OK) + { + fprintf(stderr, "dlt_procfs_log_list(): dlt_user_log_write_finish() returned error.\n"); + return ret; + } + + if((ret = dlt_user_log_write_start(&procfs_ctx, &data, config.log_level)) < DLT_RETURN_OK) + { + fprintf(stderr, "dlt_procfs_log_list(): dlt_user_log_write_start() returned error.\n"); + return ret; + } + + if((ret = dlt_user_log_write_string(&data, title)) < DLT_RETURN_OK) + { + fprintf(stderr, "dlt_procfs_log_list(): dlt_user_log_write_string() returned error.\n"); + return ret; + } + } + else if(delete_elements) + { + if((ret = dlt_procfs_remove_process_at_cursor(list)) < DLT_RETURN_OK) + return ret; + } + else + { + list->cursor = list->cursor->next; + } + } + while(list->cursor != NULL); + + if((ret = dlt_user_log_write_finish(&data)) < DLT_RETURN_OK) + { + fprintf(stderr, "dlt_procfs_log_list(): dlt_user_log_write_finish() returned error.\n"); + return ret; + } + + return DLT_RETURN_OK; +} + +DltReturnValue dlt_procfs_update_process_list(DltProcfsProcessList *list, unsigned long int time_dif_ms) +{ + static char *strchk; + static DltReturnValue tmp_ret; + static struct dirent *current_dir; + static pid_t current_dir_pid; + + if(list == NULL) + { + fprintf(stderr, "dlt_procfs_update_process_list(): Nullpointer parameter"); + return DLT_RETURN_WRONG_PARAMETER; + } + + DIR *proc_dir = opendir("/proc"); + if(proc_dir == NULL) + { + dlt_log(LOG_ERR, "Could not open /proc/ !\n"); + return DLT_RETURN_ERROR; + } + + current_dir = readdir(proc_dir); + dlt_procfs_reset_cursor(list); + + int debug_process_count = 0; + + if(pthread_mutex_lock(&process_list_mutex) < 0) + { + fprintf(stderr, "Can't lock mutex\n"); + return DLT_RETURN_ERROR; + } + + while(1) + { + if(current_dir == NULL) + { + /* no more active processes.. delete all remaining processes in the list */ + if(list->cursor != NULL) + while(list->cursor != NULL) + { + if((tmp_ret = dlt_procfs_add_process_after_cursor(stopped_process_list, dlt_procfs_clone_process(list->cursor))) < DLT_RETURN_OK) + return tmp_ret; + + dlt_procfs_remove_process_at_cursor(list); + } + + break; + } + + current_dir_pid = strtol(current_dir->d_name, &strchk, 10); + if(*strchk != '\0' || current_dir_pid <= 0) + { + /* no valid PID */ + current_dir = readdir(proc_dir); // next process in proc-fs + continue; + } + + /* compare the /proc/-filesystem with our process-list */ + if(list->cursor == NULL || current_dir_pid < list->cursor->pid) // New Process + { + DltProcfsProcess *new_process = dlt_procfs_create_process(current_dir_pid); + if(new_process == NULL) + { + fprintf(stderr, "Error: Could not create process (out of memory?)\n"); + return DLT_RETURN_ERROR; + } + + if((tmp_ret = dlt_procfs_add_process_before_cursor(list, new_process)) < DLT_RETURN_OK) + return tmp_ret; + + if((tmp_ret = dlt_procfs_add_process_before_cursor(new_process_list, dlt_procfs_clone_process(new_process))) < DLT_RETURN_OK) + return tmp_ret; + + current_dir = readdir(proc_dir); // next process in proc-fs + debug_process_count++; + } + else if(current_dir_pid > list->cursor->pid) // Process ended + { + if((tmp_ret = dlt_procfs_add_process_after_cursor(stopped_process_list, dlt_procfs_clone_process(list->cursor))) < DLT_RETURN_OK) + return tmp_ret; + + if((tmp_ret = dlt_procfs_remove_process_at_cursor(list)) < DLT_RETURN_OK) + return tmp_ret; + } + else if(current_dir_pid == list->cursor->pid) // Staying process + { + /* update data */ + if((tmp_ret = dlt_procfs_update_process(list->cursor, time_dif_ms)) < DLT_RETURN_OK) + return tmp_ret; + + if(list->cursor->cpu_time > 0) // only log active processes + if((tmp_ret = dlt_procfs_add_process_after_cursor(update_process_list, dlt_procfs_clone_process(list->cursor))) < DLT_RETURN_OK) + { + fprintf(stderr, "dlt_procfs_update_process_list: Can't add process to list updateProcessList\n"); + return tmp_ret; + } + + if((tmp_ret = dlt_procfs_increment_cursor(list)) < DLT_RETURN_OK) // next process in list + return tmp_ret; + + current_dir = readdir(proc_dir); // next process in proc-fs + debug_process_count++; + } + } + + if(pthread_mutex_unlock(&process_list_mutex) < 0) + { + fprintf(stderr, "Can't unlock mutex\n"); + return DLT_RETURN_ERROR; + } + + /* Log new processes */ + if((tmp_ret = dlt_procfs_log_list(new_process_list, &dlt_procfs_get_msg_process_new, "NEW", 1)) < DLT_RETURN_OK) + return tmp_ret; + + /* Log stopped processes */ + if((tmp_ret = dlt_procfs_log_list(stopped_process_list, &dlt_procfs_get_msg_process_stop, "STP", 1)) < DLT_RETURN_OK) + return tmp_ret; + + /* Log active processes */ + if((tmp_ret = dlt_procfs_log_list(update_process_list, &dlt_procfs_get_msg_process_update, "ACT", 1)) < DLT_RETURN_OK) + return tmp_ret; + + if(closedir(proc_dir) < 0) + fprintf(stderr, "Could not close /proc/ directory\n"); + + return DLT_RETURN_OK; +} + +void *dlt_procfs_start_irq_thread() +{ + if(dlt_procfs_irq_loop() < DLT_RETURN_OK) + dlt_procfs_stop_loops(-1); + + return NULL; +} + +DltReturnValue dlt_procfs_irq_loop() +{ + static unsigned long int old_millis, sleep_millis, dif_millis; + + old_millis = get_millis(); + + while(!stop_loop) + { + /*DltReturnValue ret = */ dlt_procfs_log_interrupts(&procfs_ctx, config.log_level); + //if(ret < DLT_RETURN_OK) + // return ret; + + dif_millis = get_millis() - old_millis; + + if(dif_millis >= (unsigned long)(config.irq_log_interval)) + sleep_millis = 0; + else + sleep_millis = config.irq_log_interval - dif_millis; + + usleep(sleep_millis * 1000); + + old_millis = get_millis(); + } + + return DLT_RETURN_OK; +} + +void *dlt_procfs_start_check_thread() +{ + if(dlt_procfs_check_loop() < DLT_RETURN_OK) + dlt_procfs_stop_loops(-1); + + return NULL; +} + +DltReturnValue dlt_procfs_check_loop() +{ + static unsigned long int old_millis, sleep_millis, dif_millis; + + old_millis = get_millis(); + + while(!stop_loop) + { + /*DltReturnValue ret = */ dlt_procfs_log_check_commandlines(); + //if(ret < DLT_RETURN_OK) + // return ret; + + dif_millis = get_millis() - old_millis; + + if(dif_millis >= (unsigned long)(config.check_log_interval)) + sleep_millis = 0; + else + sleep_millis = config.check_log_interval - dif_millis; + + usleep(sleep_millis * 1000); + + old_millis = get_millis(); + } + + return DLT_RETURN_OK; +} + +DltReturnValue dlt_procfs_log_check_commandlines() +{ + if(pthread_mutex_lock(&process_list_mutex) < 0) + { + fprintf(stderr, "Can't lock mutex\n"); + return DLT_RETURN_ERROR; + } + + DltReturnValue ret = dlt_procfs_log_list(list, dlt_procfs_get_msg_process_commandline, "CHK", 0); + + if(pthread_mutex_unlock(&process_list_mutex) < 0) + { + fprintf(stderr, "Can't unlock mutex\n"); + return DLT_RETURN_ERROR; + } + + return ret; +} diff --git a/src/procfs/dlt-procfs.conf b/src/procfs/dlt-procfs.conf new file mode 100644 index 0000000..5abd2a7 --- /dev/null +++ b/src/procfs/dlt-procfs.conf @@ -0,0 +1,18 @@ +# Configuration file for DLT /proc/-filesystem logger +# + +######################################################################## +# General configuration +######################################################################## + +# The interval in milliseconds of how often process updates should be logged. (Default: 1000) +process_interval = 1000 + +# The interval in milliseconds of how often interrupt stats should be logged. (Default: 3000) +irq_interval = 3000 + +# The interval in milliseconds of how often the commandlines of all processes should be logged. (Default: 10000) +check_interval = 10000 + +# The used log level. -1 = DEFAULT, 0 = OFF, [...], 6 = VERBOSE (Default: 4) +log_level = 4 \ No newline at end of file diff --git a/src/procfs/dlt-procfs.h b/src/procfs/dlt-procfs.h new file mode 100644 index 0000000..637aecb --- /dev/null +++ b/src/procfs/dlt-procfs.h @@ -0,0 +1,61 @@ +/* + * @licence app begin@ + * SPDX license identifier: MPL-2.0 + * + * Copyright (C) 2011-2015, BMW AG + * + * This file is part of GENIVI Project DLT - Diagnostic Log and Trace. + * + * This Source Code Form is subject to the terms of the + * Mozilla Public License (MPL), v. 2.0. + * If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + * + * For further information see http://www.genivi.org/. + * @licence end@ + */ + +/*! + * \author Sven Hassler + * + * \copyright Copyright © 2011-2015 BMW AG. \n + * License MPL-2.0: Mozilla Public License version 2.0 http://mozilla.org/MPL/2.0/. + * + * \file dlt-procfs.h + */ + +#ifndef SRC_PROCFS_DLT_PROCFS_H_ +#define SRC_PROCFS_DLT_PROCFS_H_ + +#include "dlt.h" +#include "dlt-procfs-common.h" +#include "dlt-procfs-process.h" +#include "dlt-procfs-process-list.h" +#include "dlt-procfs-interrupt.h" +#include + +// CONSTANT DEFINITIONS +#define DEFAULT_CONF_FILE ( CONFIGURATION_FILES_DIR "/dlt-procfs.conf") + +#define COMMAND_LINE_SIZE 1024 + +// STRUCTURES +typedef struct +{ + char *configurationFileName; + int customConfigFile; +} DltProcfsOptions; + +typedef struct +{ + int process_log_interval, irq_log_interval, check_log_interval; + DltLogLevelType log_level; +} DltProcfsConfig; + +// FUNCTION DECLARATIONS: +DltReturnValue dlt_procfs_read_command_line(DltProcfsOptions *options, int argc, char **argv); +DltReturnValue dlt_procfs_read_configuration_file(DltProcfsConfig *config, char *file_name); +void dlt_procfs_free_cli_options(DltProcfsOptions *options); +DltReturnValue dlt_procfs_init(int argc, char **argv, DltProcfsConfig *config); + +#endif /* SRC_PROCFS_DLT_PROCFS_H_ */ -- cgit v1.2.1