Livedumper is an application that can save coredump of a running process.
crash-manager got three new switches:
-l - run livedumper instead of minicoredumper and don't send notify
-k - kill process after saving the report
-r - print report path to stdout
Change-Id: If4fe21b250ae5e939d7b63ea5c4bf75bec6123a4
ADD_SUBDIRECTORY(src/log_dump)
ENDIF()
+IF("${LIVEDUMPER}" STREQUAL "ON")
+ ADD_SUBDIRECTORY(src/livedumper)
+ENDIF()
+
ADD_SUBDIRECTORY(tests)
#define MINICOREDUMPER_BIN_PATH "@MINICOREDUMPER_BIN_PATH@"
#define MINICOREDUMPER_CONFIG_PATH "@MINICOREDUMPER_CONFIG_PATH@"
#define DEBUGMODE_PATH "@DEBUGMODE_PATH@"
+#define LIVEDUMPER_BIN_PATH "@LIVEDUMPER_BIN_PATH@"
#endif /* __DEFS_H__ */
%define _with_tests on
%define _with_logdump on
+%define _with_livedumper on
%bcond_with doc
%bcond_with sys_assert
%bcond_with tests
%bcond_with logdump
+%bcond_with livedumper
# NOTE: To disable coredump set DumpCore=0 in configuration file
BuildRequires: doxygen
%endif
+%if %{with livedumper}
+BuildRequires: boost-devel
+%endif
+
Requires(post): coreutils
Requires(post): tar
Requires(post): gzip
This package contains installable tests in Bash.
%endif
+%if %{with livedumper}
+%package livedumper
+Summary: Livedumper allows to dump core of live process
+
+%description livedumper
+%endif
+
%prep
%setup -q
-DDEBUGMODE_PATH=%{debugmode_path} \
-DMINICOREDUMPER_BIN_PATH=%{_sbindir}/minicoredumper \
-DMINICOREDUMPER_CONFIG_PATH=%{_sysconfdir}/minicoredumper/minicoredumper.cfg.json \
+ -DLIVEDUMPER_BIN_PATH=%{_bindir}/livedumper \
-DCRASH_STACK_PATH=%{_libexecdir}/crash-stack \
-DCRASH_TESTS_PATH=%{_libdir}/crash-worker-tests \
-DSYS_ASSERT=%{on_off sys_assert} \
-DLOG_DUMP=%{on_off logdump} \
+ -DLIVEDUMPER=%{on_off livedumper} \
-DUPGRADE_SCRIPT_PATH=%{upgrade_script_path} \
+ -DLOGGER=dlog
make %{?jobs:-j%jobs}
%if %{with doc}
%{_libdir}/crash-worker-tests/crash_common.sh
%endif
+
+%if %{with livedumper}
+%files livedumper
+%manifest %{name}.manifest
+%{_bindir}/livedumper
+%endif
# btee.c) that will be copied to the destination directory, but we don't want
# them in the rpm package so this flag is to avoid RPM build error:
%define _unpackaged_files_terminate_build 0
+%define _with_livedumper on
+%bcond_with livedumper
Name: crash-worker_system-tests
Summary: Package with binaries and scripts for crash-worker system tests
%{_libdir}/crash-worker_system-tests/utils/kenny
%{_libdir}/crash-worker_system-tests/utils/minicore-utils.sh
%{_libdir}/crash-worker_system-tests/run.sh
+%if %{with livedumper}
+%{_libdir}/crash-worker_system-tests/livedumper/livedumper.sh
+%endif
%defattr(-,root,root)
# %post
/*
* crash-manager
*
- * Copyright (c) 2016 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2016-2019 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the License);
* you may not use this file except in compliance with the License.
#include <sys/procfs.h>
#include <sys/stat.h>
#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/ptrace.h>
#include <sys/vfs.h>
#include <unistd.h>
#define WAIT_FOR_OPT_TIMEOUT_SEC 60
#define MINICOREDUMPER_TIMEOUT_MS DEFAULT_COMMAND_TIMEOUT_MS
+#define LIVEDUMPER_TIMEOUT_MS DEFAULT_COMMAND_TIMEOUT_MS
#define CRASH_STACK_TIMEOUT_MS DEFAULT_COMMAND_TIMEOUT_MS
#define ZIP_TIMEOUT_MS DEFAULT_COMMAND_TIMEOUT_MS
char *log_path;
char appid[APPID_MAX];
char pkgid[PKGNAME_MAX];
+ bool livedump;
+ bool kill;
+ bool print_result_path;
#ifdef SYS_ASSERT
char *sysassert_cs_path;
bool have_sysassert_report;
" -i --tid=TID TID of thread that triggered core dump\n"
" -s --signal=SIG number of signal causing dump\n"
" -t --time=TIME time of dump, expressed as seconds since the Epoch\n"
+ " -l --live get coredump of running process\n"
+ " -k --kill-after-dump kill after dump (only with --live option)\n"
+ " -r --print print report path to stdout\n"
" -h --help this message\n"
+ "\n"
+ "for --live option only --pid is required\n"
"\n", name);
}
{"tid", required_argument, NULL, 'i'},
{"signal", required_argument, NULL, 's'},
{"time", required_argument, NULL, 't'},
+ {"live", no_argument, NULL, 'l'},
+ {"kill-after-dump", no_argument, NULL, 'k'},
+ {"print", no_argument, NULL, 'r'},
{"help", no_argument, NULL, 'h'},
};
- while ((opt = getopt_long(argc, argv, "p:u:g:i:s:t:h", long_options, NULL)) != -1) {
+ while ((opt = getopt_long(argc, argv, "p:u:g:i:s:t:hlkr", long_options, NULL)) != -1) {
switch (opt) {
case 'p':
GET_NUMBER(pid)
case 't':
GET_NUMBER(time)
break;
+ case 'l':
+ cinfo->livedump = true;
+ break;
+ case 'k':
+ cinfo->kill = true;
+ break;
+ case 'r':
+ cinfo->print_result_path = true;
+ break;
case 'h':
default:
print_help(argv[0]);
- result = false;
- break;
+ return false;
}
}
- if (result && (!pid_set || !uid_set || !gid_set || !sig_set)) {
+ if (!pid_set || (!cinfo->livedump && (!gid_set || !uid_set || !sig_set))) {
printf("Not enough parameters.\n\n");
print_help(argv[0]);
- result = false;
+ return false;
+ }
+
+ if (cinfo->livedump && sig_set) {
+ printf("--sig can not be used with --live option\n\n");
+ print_help(argv[0]);
+ return false;
+ }
+
+ if (!cinfo->livedump && cinfo->kill) {
+ printf("Option --kill-after-dump can be used only with --live\n");
+ return false;
}
return result;
#undef QUOTE
char date[80];
struct tm loc_tm;
+ cinfo->livedump = false;
+ cinfo->kill = false;
+ cinfo->print_result_path = false;
cinfo->tid_info = -1;
cinfo->time_info = 0;
if (!parse_args(cinfo, argc, argv))
return -1;
+ if (cinfo->livedump) {
+ if (cinfo->kill)
+ cinfo->sig_info = 9;
+ else
+ cinfo->sig_info = 0;
+ }
+
+ if (cinfo->livedump && !file_exists(LIVEDUMPER_BIN_PATH)) {
+ fprintf(stderr, "Error: %s doesn't exist - can not perform livedump. Terminating.\n", LIVEDUMPER_BIN_PATH);
+ _E("Error: %s doesn't exist - can not perform livedump. Terminating.\n", LIVEDUMPER_BIN_PATH);
+ return -1;
+ }
+
if (cinfo->tid_info == -1) {
- cinfo->tid_info = find_crash_tid(cinfo->pid_info);
- if (cinfo->tid_info < 0) {
- _I("TID not found");
+ if (cinfo->livedump) {
cinfo->tid_info = cinfo->pid_info;
+ } else {
+ cinfo->tid_info = find_crash_tid(cinfo->pid_info);
+ if (cinfo->tid_info < 0) {
+ _I("TID not found");
+ cinfo->tid_info = cinfo->pid_info;
+ }
}
}
return is_ok;
}
+static bool execute_livedumper(const struct crash_info *cinfo, int *exit_code)
+{
+ char *coredump_path = NULL;
+ char *prstatus_fd_str = NULL;
+ bool is_ok = false;
+ char pid_str[11];
+
+ if (asprintf(&coredump_path, "%s/%s.coredump", cinfo->pfx, cinfo->name) == -1 ||
+ asprintf(&prstatus_fd_str, "%d", cinfo->prstatus_fd) == -1) {
+ _E("Unable to allocate memory");
+ goto out;
+ }
+
+ SNPRINTF_OR_EXIT(pid, "%d")
+
+ /* Execute livedumper */
+ char *args[] = {
+ LIVEDUMPER_BIN_PATH, // livedumper filename path
+ "-P", prstatus_fd_str,
+ "-f", coredump_path,
+ pid_str, // %p - pid
+ NULL
+ };
+
+ spawn_param_s param = { .fn = spawn_setstdout, .u.int_val = STDERR_FILENO };
+ is_ok = spawn_wait(args, NULL, ¶m, LIVEDUMPER_TIMEOUT_MS, exit_code);
+out:
+ free(prstatus_fd_str);
+ free(coredump_path);
+ return is_ok;
+}
+
static bool execute_crash_stack(const struct crash_info *cinfo, int *exit_code)
{
char pid_str[11], tid_str[11], sig_str[11], prstatus_fd_str[11];
#undef SNPRINTF_OR_EXIT
#undef SNPRINTF_OR_EXIT_W
+static bool kill_pid(const pid_t pid, int sig, bool wait)
+{
+ if (kill(pid, sig) == -1) {
+ _E("kill sig %d error: %m\n", sig);
+ return false;
+ }
+
+ if (wait) {
+ if (waitpid(pid, NULL, 0) == -1) {
+ _E("waitpid error: %m");
+ return false;
+ }
+ }
+ return true;
+}
+
+static bool process_stop(const pid_t pid)
+{
+ _D("stop process %d", pid);
+ return kill_pid(pid, SIGSTOP, false);
+}
+
+static bool process_continue(const pid_t pid)
+{
+ _D("continue process %d", pid);
+ return kill_pid(pid, SIGCONT, false);
+}
+
static bool execute_crash_modules(struct crash_info *cinfo)
{
int exit_code = 0;
- if (!execute_minicoredump(cinfo, &exit_code) || exit_code != 0) {
- _E("Failed to run minicoredumper - can not continue");
- return false;
+ if (cinfo->livedump) {
+ _I("Starting the livedumper");
+ if (!process_stop(cinfo->pid_info))
+ return false;
+ if (!execute_livedumper(cinfo, &exit_code) || exit_code != 0) {
+ _E("Failed to run livedumper - can not continue");
+ process_continue(cinfo->pid_info);
+ return false;
+ }
+ } else {
+ _I("Starting the minicoredumper");
+ if (!execute_minicoredump(cinfo, &exit_code) || exit_code != 0) {
+ _E("Failed to run minicoredumper - can not continue");
+ return false;
+ }
}
#ifdef SYS_ASSERT
return false;
#endif
execute_crash_stack(cinfo, NULL);
+ if (cinfo->livedump)
+ process_continue(cinfo->pid_info);
return true;
}
move_dump_data(cinfo.info_path, &cinfo);
}
- /* Release the core pipe as passed by kernel, allowing another
- * coredump to be handled.
- *
- * Due to usage of core_pipe_limit there is limited number of
- * crash-manager processes that kernel is going to invoke
- * concurrently. As the next and last step is a _synchronous_
- * call to crash-popup we close the descriptor here.
- *
- * Note: for VIP processes this will likely cause the system
- * to reboot without showing popup.
- */
- close(STDIN_FILENO);
+ if (cinfo.print_result_path)
+ printf("REPORT_PATH=%s\n", cinfo.result_path);
+
+ if (!cinfo.livedump) {
+ /* Release the core pipe as passed by kernel, allowing another
+ * coredump to be handled.
+ *
+ * Due to usage of core_pipe_limit there is limited number of
+ * crash-manager processes that kernel is going to invoke
+ * concurrently. As the next and last step is a _synchronous_
+ * call to crash-popup we close the descriptor here.
+ *
+ * Note: for VIP processes this will likely cause the system
+ * to reboot without showing popup.
+ */
+ close(STDIN_FILENO);
- launch_dbus_notify(&cinfo);
+ launch_dbus_notify(&cinfo);
- /* launch crash-popup only if the .debugmode file exists */
- if (debug_mode)
- launch_crash_popup(&cinfo);
+ /* launch crash-popup only if the .debugmode file exists */
+ if (debug_mode)
+ launch_crash_popup(&cinfo);
+ } else if (cinfo.kill) {
+ kill_pid(cinfo.pid_info, SIGKILL, false);
+ }
exit:
_I("Exiting with exit code %d", res);
--- /dev/null
+cmake_minimum_required(VERSION 2.6)
+project(livedumper CXX)
+
+set(LIVEDUMPER_BIN "livedumper")
+
+find_package( Boost 1.58 COMPONENTS system REQUIRED)
+include_directories ( ${Boost_INCLUDE_DIR} )
+set(PREFIX ${CMAKE_INSTALL_PREFIX})
+add_definitions(-std=c++11)
+
+# add_executable(${LIVEDUMPER_BIN} main.cpp)
+
+if("${LOGGER}" STREQUAL "dlog")
+ set(LOGGER_FILE dlog.cpp)
+else()
+ set(LOGGER_FILE clog.cpp)
+endif(DLOG)
+
+set(LIVEDUMPER_SRCS main.cpp ${LOGGER_FILE})
+add_executable(${LIVEDUMPER_BIN} ${LIVEDUMPER_SRCS})
+
+if("${LOGGER}" STREQUAL "dlog")
+ include(FindPkgConfig)
+ pkg_check_modules(DLOG_REQUIRED dlog)
+ set_property(TARGET ${LIVEDUMPER_BIN} APPEND_STRING PROPERTY COMPILE_FLAGS ${DLOG_CFLAGS})
+ target_link_libraries(${LIVEDUMPER_BIN} ${DLOG_LIBRARIES})
+endif()
+
+install (TARGETS ${LIVEDUMPER_BIN} DESTINATION bin)
--- /dev/null
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include "log.hpp"
+
+#include <unistd.h>
+
+#include <cstdarg>
+#include <iostream>
+
+void Logger::log(const LogMsgType type, const char *fmt...) {
+ va_list args;
+ int fd = (type == LogMsgType::ERROR) ? STDERR_FILENO : STDOUT_FILENO;
+
+ va_start(args, fmt);
+ vdprintf(fd, fmt, args);
+ dprintf(fd, "\n");
+ va_end(args);
+}
+
+Logger logger;
--- /dev/null
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#ifndef CORE_HPP__
+#define CORE_HPP__
+
+#include "helpers.hpp"
+#include "log.hpp"
+#include "note.hpp"
+#include "program.hpp"
+
+#include <fcntl.h>
+#include <libelf.h>
+#include <limits.h>
+#include <sys/procfs.h>
+
+#include <cstring>
+#include <iostream>
+#include <memory>
+#include <string>
+#include <vector>
+
+
+template <typename T>
+class Core {
+ private:
+ static constexpr size_t PAGESIZE = 0x1000;
+ ProgramTableEntryBase<T> *m_note_header;
+ std::vector<std::unique_ptr<ProgramTableEntryBase<T>>> m_phdrt;
+ size_t current_data_address;
+ std::vector <std::unique_ptr<Note>> m_notes;
+ typename T::Ehdr m_ehdr;
+
+ public:
+ Core() : m_note_header(new ProgramTableEntryNote<T>()),
+ current_data_address(sizeof(m_ehdr)) {
+ m_phdrt.push_back(std::unique_ptr<ProgramTableEntryBase<T>>(m_note_header));
+ }
+
+ void FillELFHeader() {
+ memcpy(m_ehdr.e_ident, ELFMAG, sizeof(ELFMAG));
+ m_ehdr.e_ident[EI_CLASS] = T::ELFCLASS;
+ m_ehdr.e_ident[EI_DATA] = ELFDATA2LSB;
+ m_ehdr.e_ident[EI_VERSION] = EV_CURRENT;
+ m_ehdr.e_ident[EI_OSABI] = ELFOSABI_SYSV;
+ m_ehdr.e_ident[EI_ABIVERSION] = 0;
+ memset(&m_ehdr.e_ident[EI_PAD], 0, sizeof(m_ehdr.e_ident) - EI_PAD);
+ m_ehdr.e_type = ET_CORE;
+ m_ehdr.e_machine = MACHINE;
+ m_ehdr.e_version = EV_CURRENT;
+ m_ehdr.e_entry = 0;
+ m_ehdr.e_phoff = sizeof(m_ehdr);
+ m_ehdr.e_shoff = 0;
+ m_ehdr.e_flags = 0;
+ m_ehdr.e_ehsize = sizeof(m_ehdr);
+ m_ehdr.e_phentsize = sizeof(typename T::Phdr);
+ m_ehdr.e_phnum = m_phdrt.size();
+ m_ehdr.e_shentsize = 0;
+ m_ehdr.e_shnum = 0;
+ m_ehdr.e_shstrndx = 0;
+ }
+
+ void ImportMapRecord(std::shared_ptr<MapRecord> record) {
+ ProgramTableEntryLoad<T> *entry = new ProgramTableEntryLoad<T>();
+ entry->header.p_flags = static_cast<decltype(entry->header.p_flags)>(record->perms);
+ entry->header.p_offset = current_data_address;
+ current_data_address += record->end - record->start;
+ entry->header.p_vaddr = record->start;
+ entry->header.p_paddr = 0;
+ entry->header.p_filesz = record->end - record->start;
+ entry->header.p_memsz = record->end - record->start;
+ entry->header.p_align = PAGESIZE;
+ m_phdrt.push_back(std::unique_ptr<ProgramTableEntryLoad<T>>(entry));
+ }
+
+ void SaveNotes(std::ofstream &core_file) {
+ for (auto const ¬e : m_notes)
+ note->SaveTo(core_file);
+ }
+
+ void SaveELFHeader(std::ofstream &core_file) {
+ FillELFHeader();
+ core_file.write(reinterpret_cast<char*>(&m_ehdr), sizeof(m_ehdr));
+ }
+
+ void SaveProgramHeadersTable(std::ofstream &core_file) {
+ for (const auto& record : m_phdrt) {
+ core_file.write(reinterpret_cast<char*>(&record->header),
+ sizeof(record->header));
+ }
+ }
+
+ bool IsEmpty(char *buff, size_t size) {
+ uint32_t *tmp_buff = reinterpret_cast<uint32_t*>(buff);
+ const size_t divider = sizeof(tmp_buff[0])/sizeof(buff[0]);
+
+ assert(size % divider == 0);
+
+ for (size_t i = 0; i < size/divider; i++) {
+ if (tmp_buff[i] != 0)
+ return false;
+ }
+ return true;
+ }
+
+ void CopyData(int input, std::ofstream &output, size_t size) {
+ size_t len = PAGESIZE;
+ char buff[PAGESIZE];
+
+ while (size > 0) {
+ if (size < len)
+ len = size;
+
+ ssize_t cur_pos = lseek64(input, 0, SEEK_CUR);
+
+ ssize_t bytes_read;
+ if ((bytes_read = read(input, buff, len)) == -1) {
+ // tell() returns 128bits value so I cast this to 64bit to be able to print it
+ logger.log_error("read error at 0x%llx: %s\n", static_cast<long long int>(output.tellp()),
+ std::system_category().default_error_condition(errno).message());
+ lseek64(input, cur_pos + len, SEEK_SET);
+ output.seekp(len, std::ios_base::cur);
+ size -= len;
+ } else {
+ if (bytes_read == PAGESIZE && IsEmpty(buff, len)) {
+ output.seekp(bytes_read, std::ios_base::cur);
+ } else {
+ output.write(buff, bytes_read);
+ }
+ size -= bytes_read;
+ }
+ }
+ }
+
+ void SaveLoadable(const std::string &mem_path, std::ofstream &core_file) {
+ int fd = open(mem_path.c_str(), O_RDONLY);
+ if (fd == -1)
+ throw std::system_error(errno, std::system_category(), "failed to open mem file " + mem_path);
+
+ for (const auto &record : m_phdrt) {
+ if (record->header.p_type != PT_LOAD)
+ continue;
+ if (lseek64(fd, record->header.p_vaddr, SEEK_SET) == -1)
+ logger.log_error("lseek64 error: %s", std::system_category().default_error_condition(errno).message());
+ CopyData(fd, core_file, record->header.p_filesz);
+ }
+ close(fd);
+ }
+
+ void AddNote(Note *note) {
+ m_note_header->header.p_filesz += note->GetSize();
+ m_note_header->header.p_memsz += note->GetSize();
+ m_notes.push_back(std::unique_ptr<Note>(note));
+ }
+
+ void AddPRSTATUSNote(const user_regs_struct ®s, const pid_t pid) {
+ NoteCorePRStatus *note = new NoteCorePRStatus(regs, pid);
+ AddNote(note);
+ }
+
+ void AddFILENote(const std::vector<std::shared_ptr<MapRecord>> &records) {
+ NoteCoreFile<T> *note = new NoteCoreFile<T>();
+ note->ImportRecords(records);
+ AddNote(note);
+ }
+
+ void AddAUXVNote(const std::vector<char> &auxv_vector) {
+ NoteCoreAUXV *note = new NoteCoreAUXV();
+ note->SetAuxv(auxv_vector);
+ AddNote(note);
+ }
+
+ void ImportMapRecords(const std::vector<std::shared_ptr<MapRecord>> &records) {
+ current_data_address += (records.size() + 1)*sizeof(typename T::ProgramTableHeader);
+ m_note_header->header.p_offset = current_data_address;
+
+ for (const auto ¬e : m_notes)
+ current_data_address += note->GetSize();
+
+ for (const auto &record : records)
+ ImportMapRecord(record);
+ }
+};
+
+#endif // CORE_HPP__
--- /dev/null
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include "log.hpp"
+
+#include <dlog.h>
+#include <unistd.h>
+
+#include <cstdarg>
+#include <iostream>
+
+void Logger::log(const LogMsgType type, const char *fmt...) {
+ va_list args;
+ log_priority prio;
+
+ switch (type) {
+ case LogMsgType::ERROR:
+ prio = DLOG_ERROR;
+ break;
+ case LogMsgType::WARNING:
+ prio = DLOG_WARN;
+ break;
+ default:
+ prio = DLOG_INFO;
+ break;
+ }
+ va_start(args, fmt);
+ vprint_log(prio, "LIVEDUMPER", fmt, args);
+ va_end(args);
+}
+
+Logger logger;
--- /dev/null
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#ifndef HELPERS_HPP__
+#define HELPERS_HPP__
+
+#include <libelf.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <functional>
+#include <string>
+#include <unistd.h>
+
+#pragma pack(push, 1)
+
+struct Notefile32 {
+ uint32_t start;
+ uint32_t end;
+ uint32_t file_ofs;
+};
+
+struct Notefile64 {
+ uint64_t start;
+ uint64_t end;
+ uint64_t file_ofs;
+};
+
+struct ProgramTableHeader32 {
+ uint32_t p_type;
+ Elf32_Off p_offset;
+ Elf32_Addr p_vaddr;
+ Elf32_Addr p_paddr;
+ uint32_t p_filesz;
+ uint32_t p_memsz;
+ uint32_t p_flags;
+ uint32_t p_align;
+};
+
+struct ProgramTableHeader64 {
+ uint32_t p_type;
+ uint32_t p_flags;
+ Elf64_Off p_offset;
+ Elf64_Addr p_vaddr;
+ Elf64_Addr p_paddr;
+ uint64_t p_filesz;
+ uint64_t p_memsz;
+ uint64_t p_align;
+};
+#pragma pack(pop)
+
+struct Elf32 {
+ typedef Elf32_Ehdr Ehdr;
+ typedef Elf32_Phdr Phdr;
+ typedef Elf32_Addr Addr;
+ typedef Elf32_Off Off;
+ typedef Elf32_Section Section;
+ typedef Elf32_Versym Versym;
+ typedef Elf32_Half Half;
+ typedef Elf32_Sword Sword;
+ typedef Elf32_Word Word;
+ typedef Elf32_Sxword Sxword;
+ typedef Elf32_Xword Xword;
+ typedef ProgramTableHeader32 ProgramTableHeader;
+ typedef Notefile32 Notefile;
+ typedef uint32_t uint;
+ static const char ELFCLASS = ELFCLASS32;
+};
+
+struct Elf64 {
+ typedef Elf64_Ehdr Ehdr;
+ typedef Elf64_Phdr Phdr;
+ typedef Elf64_Addr Addr;
+ typedef Elf64_Off Off;
+ typedef Elf64_Section Section;
+ typedef Elf64_Versym Versym;
+ typedef Elf64_Half Half;
+ typedef Elf64_Sword Sword;
+ typedef Elf64_Word Word;
+ typedef Elf64_Sxword Sxword;
+ typedef Elf64_Xword Xword;
+ typedef ProgramTableHeader64 ProgramTableHeader;
+ typedef Notefile64 Notefile;
+ typedef uint64_t uint;
+ static const char ELFCLASS = ELFCLASS64;
+};
+
+class Guardian {
+ private:
+ const std::function<void()> m_start;
+ const std::function<void()> m_end;
+ public:
+ Guardian(const std::function<void()> &start, const std::function<void()> &end)
+ : m_start(start), m_end(end) { m_start(); }
+ ~Guardian() { m_end(); }
+};
+
+bool fileExists(const std::string &path) {
+ struct stat buf;
+ return stat(path.c_str(), &buf) == 0;
+}
+
+#endif // HELPERS_HPP__
--- /dev/null
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#ifndef LIVEDUMPER_HPP__
+#define LIVEDUMPER_HPP__
+
+#include "core.hpp"
+#include "helpers.hpp"
+#include "log.hpp"
+#include "maps.hpp"
+
+#include <dirent.h>
+#include <limits.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/ptrace.h>
+#include <sys/wait.h>
+
+#include <fstream>
+#include <iostream>
+#include <stdexcept>
+#include <string>
+#include <vector>
+
+template <typename T>
+class LiveDumper {
+ private:
+ pid_t m_pid;
+ std::vector<pid_t> m_tpids;
+ Core<T> m_core;
+ std::ofstream m_core_file;
+ int prstatus_fd = -1;
+
+ public:
+ explicit LiveDumper(const pid_t pid) : m_pid(pid) {}
+
+ void CollectTpids() {
+ m_tpids.clear();
+
+ std::string task_dir = "/proc/" + std::to_string(m_pid) + "/task";
+
+ DIR *dir = opendir(task_dir.c_str());
+ if (dir == nullptr)
+ throw std::system_error(errno, std::system_category(), "failed to open directory: " + task_dir);
+
+ dirent *ent;
+ while ((ent = readdir(dir)) != nullptr) {
+ if (ent->d_type == DT_DIR &&
+ strncmp(ent->d_name, ".", 1) != 0)
+ m_tpids.push_back(std::strtol(ent->d_name, 0, 10));
+ }
+ closedir(dir);
+ }
+
+ void AttachTo(const pid_t pid) const {
+ if (ptrace(PTRACE_ATTACH, pid, nullptr, nullptr) == -1)
+ throw std::system_error(errno, std::system_category(), "failed to attach to: " + std::to_string(pid));
+ }
+
+ void setPrstatusFd(const int prstatus_fd) {
+ this->prstatus_fd = prstatus_fd;
+ }
+
+ void Attach() {
+ AttachTo(m_pid);
+
+ waitpid(m_pid, nullptr, 0);
+
+ CollectTpids();
+
+ for (const auto &pid : m_tpids) {
+ if (pid == m_pid) continue;
+ AttachTo(pid);
+ }
+ }
+
+ void Detach() const {
+ for (const auto &pid : m_tpids) {
+ if (ptrace(PTRACE_DETACH, pid, nullptr, nullptr) == -1) {
+ logger.log_error("Detach from PID %d error: %s",
+ pid, std::system_category().default_error_condition(errno).message());
+ }
+ }
+ }
+
+ void GetRegs(user_regs_struct *registers, pid_t pid) const {
+ if (ptrace(PTRACE_GETREGS, pid, nullptr, registers) == -1)
+ throw std::system_error(errno, std::system_category(),
+ "failed to get registers for: " + std::to_string(pid));
+ }
+
+ std::vector<char> GetAuxv() {
+ auto auxv_name = "/proc/" + std::to_string(m_pid) + "/auxv";
+ std::ifstream file;
+
+ file.open(auxv_name, std::ios::binary);
+ if (!file)
+ throw std::system_error(errno, std::system_category(),
+ "failed to open auxv file " + auxv_name);
+
+ std::vector<char> auxv(std::istreambuf_iterator<char>{file}, {});
+ return auxv;
+ }
+
+ void OpenCoredump(const std::string &path) {
+ m_core_file.open(path, std::ios::binary);
+ if (!m_core_file)
+ throw std::system_error(errno, std::system_category(),
+ "failed to open coredump file " + path);
+ }
+
+ void CloseCoredump() {
+ m_core_file.close();
+ }
+
+ const std::vector<std::shared_ptr<MapRecord>> ReadMaps() {
+ Maps maps(m_pid);
+ maps.Parse();
+ return maps.Records();
+ }
+
+ void SaveRegsToPrStatusFd(const user_regs_struct ®isters) {
+ prstatus_t prstatus;
+
+ memset(&prstatus, 0, sizeof(prstatus));
+ memcpy(&prstatus.pr_reg, ®isters, sizeof(prstatus.pr_reg));
+ prstatus.pr_pid = m_pid;
+
+ auto buff = reinterpret_cast<char*>(mmap(nullptr, sizeof(prstatus),
+ PROT_READ | PROT_WRITE,
+ MAP_SHARED,
+ prstatus_fd,
+ 0));
+ if (buff == MAP_FAILED)
+ return;
+ memcpy(buff, &prstatus, sizeof(prstatus));
+ }
+
+ void AddNotes(const std::vector<std::shared_ptr<MapRecord>> &records) {
+ user_regs_struct registers;
+ GetRegs(®isters, m_pid);
+
+ if (prstatus_fd >= 0)
+ SaveRegsToPrStatusFd(registers);
+
+ m_core.AddPRSTATUSNote(registers, m_pid);
+
+ for (const auto &pid : m_tpids) {
+ if (pid == m_pid)
+ continue;
+ GetRegs(®isters, pid);
+ m_core.AddPRSTATUSNote(registers, pid);
+ }
+
+ m_core.AddFILENote(records);
+ std::vector<char> auxv = GetAuxv();
+ m_core.AddAUXVNote(auxv);
+ }
+
+ bool DumpCore(const std::string &path) {
+ bool result = true;
+
+ try {
+ Guardian guard_open_coredump([this, &path]() { OpenCoredump(path);},
+ [this]() { CloseCoredump(); });
+ Guardian guard_attach([this]() { Attach(); },
+ [this]() { Detach(); });
+
+ const std::vector<std::shared_ptr<MapRecord>> records = ReadMaps();
+
+ AddNotes(records);
+ m_core.ImportMapRecords(records);
+ m_core.SaveELFHeader(m_core_file);
+ m_core.SaveProgramHeadersTable(m_core_file);
+ m_core.SaveNotes(m_core_file);
+ std::string mem_path = std::string("/proc/" + std::to_string(m_pid) + "/mem");
+ m_core.SaveLoadable(mem_path, m_core_file);
+ } catch (std::system_error &e) {
+ logger.log_error("DumpCore: %s", e.what());
+ result = false;
+ }
+
+ return result;
+ }
+};
+
+#endif // LIVEDUMPER_HPP__
--- /dev/null
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#ifndef LOG_HPP__
+#define LOG_HPP__
+
+enum class LogMsgType {
+ INFO,
+ WARNING,
+ ERROR
+};
+
+class Logger {
+ public:
+ void log(const LogMsgType type, const char *fmt...);
+};
+
+#define log_info(fmt, args...) log(LogMsgType::INFO, fmt, args)
+#define log_warning(fmt, args...) log(LogMsgType::WARNING, fmt, args)
+#define log_error(fmt, args...) log(LogMsgType::ERROR, fmt, args)
+
+extern Logger logger;
+
+#endif // LOG_HPP__
--- /dev/null
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include "livedumper.hpp"
+
+#include <libelf.h>
+#include <unistd.h>
+
+#if defined(__arm__) || defined(__i386__)
+#define ELF_TYPE Elf32
+#elif defined(__x86_64__) || defined(__aarch64__)
+#define ELF_TYPE Elf64
+#endif
+
+void help(const char *app_name) {
+ std::cout << "Usage: " << std::endl
+ << " " << app_name << " [-f file_name] [-P fd] <pid>" << std::endl
+ << std::endl
+ << " -f <file_name> output path" << std::endl
+ << " -P <fd> descriptor nr" << std::endl;
+}
+
+int main(int argc, char *argv[]) {
+ int opt;
+ int prstatus_fd = -1;
+
+ std::string output_file;
+
+ while ((opt = getopt(argc, argv, "hP:f:")) != -1) {
+ switch (opt) {
+ case 'P':
+ prstatus_fd = atoi(optarg);
+ break;
+ case 'h':
+ help(argv[0]);
+ exit(EXIT_SUCCESS);
+ case 'f':
+ output_file = optarg;
+ break;
+ default:
+ help(argv[0]);
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ if (argc <= optind) {
+ std::cout << "PID not provided" << std::endl;
+ return -1;
+ }
+
+ std::string pid_str = std::string(argv[optind]);
+
+ if (output_file.empty())
+ output_file = "livecore." + pid_str;
+
+ LiveDumper<ELF_TYPE> dumper(std::stoi(pid_str));
+
+ if (prstatus_fd > 0)
+ dumper.setPrstatusFd(prstatus_fd);
+
+ return dumper.DumpCore(output_file) ? EXIT_SUCCESS : EXIT_FAILURE;
+}
--- /dev/null
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#ifndef MAPS_HPP__
+#define MAPS_HPP__
+
+#include <boost/algorithm/string/predicate.hpp>
+#include <boost/utility/string_ref.hpp>
+#include <fstream>
+#include <iostream>
+#include <memory>
+#include <stdexcept>
+#include <string>
+#include <vector>
+
+enum class Permissions {
+ None = 0,
+ Exec = 1 << 0,
+ Write = 1 << 1,
+ Read = 1 << 2,
+ Priv = 1 << 3
+};
+
+inline Permissions operator|(Permissions a, Permissions b) {
+ return static_cast<Permissions>(static_cast<int>(a) | static_cast<int>(b));
+}
+
+inline Permissions operator&(Permissions a, Permissions b) {
+ return static_cast<Permissions>(static_cast<int>(a) & static_cast<int>(b));
+}
+
+struct MapRecord {
+ uintptr_t start;
+ uintptr_t end;
+ uintptr_t offset;
+ Permissions perms;
+ std::string path;
+
+ bool IsLoadable() const {
+ // we need only readable records
+ bool result = (perms & Permissions::Read) == Permissions::Read;
+
+ // we don't need [vvar] becasue there are problems with reading
+ // on x86, and [vvar] doesn't contain useful data to stack resolving
+ result &= path != "[vvar]";
+
+ // we don't need executable, unless it is [vsyscall] or [vdso]
+ // https://lwn.net/Articles/615809/
+ result &= ((perms & Permissions::Exec) != Permissions::Exec) ||
+ (path == "[vsyscall]" || path == "[vdso]");
+
+ result &= !boost::algorithm::ends_with(path, "locale-archive");
+
+ return result;
+ }
+};
+
+class Maps {
+ private:
+ const pid_t m_pid;
+ std::vector<std::shared_ptr<MapRecord>> m_map_records;
+
+ public:
+ explicit Maps(const pid_t pid) : m_pid(pid) {}
+
+ const std::vector<std::shared_ptr<MapRecord>> &Records() const {
+ return m_map_records;
+ }
+
+ void Parse() {
+ const std::string& maps_file_name = "/proc/" + std::to_string(m_pid) + "/maps";
+
+ std::ifstream maps_file(maps_file_name);
+ if (!maps_file)
+ throw std::system_error(errno, std::system_category(), "failed to open mem file " + maps_file_name);
+
+ std::string line;
+ while (std::getline(maps_file, line)) {
+ std::shared_ptr<MapRecord> record = ParseLine(line);
+
+ if (record->IsLoadable())
+ m_map_records.push_back(record);
+ }
+ }
+
+ // Parse line of /proc/<pid>/maps file that has the format specifier:
+ //
+ // "%08lx-%08lx %c%c%c%c %08llx %02x:%02x %lu "
+ //
+ std::shared_ptr<MapRecord> ParseLine(boost::string_ref line) {
+ static const char FIELD_DELIMITER = ' ';
+ std::shared_ptr<MapRecord> record = std::make_shared<MapRecord>();
+
+ auto getNextToken = [&line](const char delim) -> boost::string_ref {
+ auto pos = line.find_first_of(delim);
+ auto result = line.substr(0, pos);
+ line = line.substr(pos + 1);
+ return result;
+ };
+
+ // START
+ record->start = std::stoull(std::string(getNextToken('-')), nullptr, 16);
+
+ // END
+ record->end = std::stoull(std::string(getNextToken(FIELD_DELIMITER)), nullptr, 16);
+
+ // PERMS
+ auto token = getNextToken(FIELD_DELIMITER);
+ record->perms = Permissions::None;
+
+ auto storePerm = [&record, &token](const char c, const Permissions perm) {
+ if (token.find(c) != std::string::npos)
+ record->perms = record->perms | perm;
+ };
+
+ storePerm('r', Permissions::Read);
+ storePerm('w', Permissions::Write);
+ storePerm('x', Permissions::Exec);
+ storePerm('p', Permissions::Priv);
+
+ // OFFSET
+ record->offset = std::stoull(std::string(getNextToken(FIELD_DELIMITER)), nullptr, 16);
+
+ // DEV - ignore
+ getNextToken(FIELD_DELIMITER);
+
+ // INODE - ignore
+ getNextToken(FIELD_DELIMITER);
+
+ // PATH
+ size_t delim = line.find_first_not_of(' ');
+ if (delim != std::string::npos) {
+ line = line.substr(delim);
+ record->path = std::string(line);
+ } else {
+ record->path.clear();
+ }
+ return record;
+ }
+};
+
+#endif // MAPS_HPP__
--- /dev/null
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#ifndef NOTE_HPP__
+#define NOTE_HPP__
+
+#include "helpers.hpp"
+#include "maps.hpp"
+
+#include <string.h>
+#include <sys/procfs.h>
+
+#include <fstream>
+#include <iostream>
+#include <memory>
+#include <string>
+#include <vector>
+
+#if defined(__arm__)
+typedef user_regs user_regs_struct;
+#define MACHINE EM_ARM
+#elif defined(__x86_64__)
+#define MACHINE EM_X86_64
+#elif defined(__i386__)
+#define MACHINE EM_386
+#endif
+
+class Note {
+ public:
+ // man elf(5): name and desc starting address has a 4 byte alignment.
+ static constexpr unsigned int PADDING = 4;
+ uint32_t name_size;
+ uint32_t desc_size;
+ uint32_t type;
+ std::string name;
+
+ explicit Note(uint32_t type) : name_size(0), desc_size(0), type(type) {}
+ virtual ~Note() {}
+
+ virtual void SaveDataTo(std::ofstream &core_file) const {}
+ virtual size_t GetSize() const {
+ size_t size = 0;
+ size += sizeof(name_size);
+ size += name_size + CalculatePadding<Note::PADDING>(name_size);
+ size += sizeof(desc_size);
+ size += desc_size + CalculatePadding<Note::PADDING>(desc_size);
+ size += sizeof(type);
+ return size;
+ }
+
+ template <int to>
+ static size_t CalculatePadding(size_t value) {
+ static_assert((to & (to - 1)) == 0, "must be power of 2");
+ return (to - value) & (to - 1);
+ }
+
+ void Pad(std::ofstream &core_file, size_t value) const {
+ static const char b = 0;
+ for (size_t i = 0; i < value; i++)
+ core_file.write(&b, 1);
+ }
+
+ void SaveTo(std::ofstream &core_file) const {
+ core_file.write(reinterpret_cast<const char*>(&name_size), sizeof(name_size));
+ core_file.write(reinterpret_cast<const char*>(&desc_size), sizeof(desc_size));
+ core_file.write(reinterpret_cast<const char*>(&type), sizeof(type));
+ core_file.write(name.c_str(), name_size);
+ Pad(core_file, CalculatePadding<Note::PADDING>(name_size));
+ SaveDataTo(core_file);
+ }
+};
+
+class NoteCorePRStatus : public Note {
+ private:
+ prstatus_t m_prstatus;
+ public:
+ NoteCorePRStatus(const user_regs_struct ®s, const pid_t pid) : Note(NT_PRSTATUS) {
+ memset(&m_prstatus, 0, sizeof(m_prstatus));
+ name = "CORE";
+ name_size = name.length() + 1;
+ desc_size = sizeof(m_prstatus);
+ m_prstatus.pr_pid = pid;
+
+ memcpy(&m_prstatus.pr_reg, ®s, sizeof(m_prstatus.pr_reg));
+ }
+
+ void SaveDataTo(std::ofstream &core_file) const {
+ core_file.write(reinterpret_cast<const char*>(&m_prstatus), sizeof(m_prstatus));
+ Pad(core_file, CalculatePadding<Note::PADDING>(desc_size));
+ }
+};
+
+template <typename T>
+class NoteCoreFile : public Note {
+ private:
+ typename T::uint count;
+ typename T::uint page_size;
+ std::vector<std::unique_ptr<typename T::Notefile>> files;
+ std::vector<std::string> names;
+
+ public:
+ NoteCoreFile() : Note(NT_FILE), count(0), page_size(0) { }
+
+ void ImportRecords(const std::vector<std::shared_ptr<MapRecord>> &records) {
+ name = "CORE";
+ name_size = name.length() + 1;
+ desc_size = 2*sizeof(typename T::uint);
+
+ for (const auto &record : records) {
+ if (!fileExists(record->path))
+ continue;
+ desc_size += sizeof(typename T::Notefile);
+ auto *entry = new typename T::Notefile();
+ entry->start = record->start;
+ entry->end = record->end;
+ entry->file_ofs = record->offset;
+ files.push_back(std::unique_ptr<typename T::Notefile>(entry));
+
+ desc_size += record->path.length() + 1;
+ names.push_back(record->path);
+ }
+ }
+
+ void SaveDataTo(std::ofstream &core_file) const {
+ typename T::uint cc = files.size();
+ typename T::uint page_size = 1;
+
+ core_file.write(reinterpret_cast<const char*>(&cc),
+ sizeof(typename T::uint));
+ core_file.write(reinterpret_cast<const char*>(&page_size),
+ sizeof(typename T::uint));
+
+ for (const auto &file : files)
+ core_file.write(reinterpret_cast<const char*>(file.get()), sizeof(*file));
+
+ for (const auto &name : names)
+ core_file.write(reinterpret_cast<const char*>(name.c_str()), name.length() + 1);
+
+ Pad(core_file, CalculatePadding<Note::PADDING>(desc_size));
+ }
+};
+
+class NoteCoreAUXV : public Note {
+ private:
+ std::vector<char> m_auxv;
+
+ public:
+ NoteCoreAUXV() : Note(NT_AUXV) { }
+ void SetAuxv(const std::vector<char> &auxv) {
+ name = "CORE";
+
+ m_auxv = auxv;
+
+ name_size = name.length() + 1;
+ desc_size = m_auxv.size();
+ }
+ void SaveDataTo(std::ofstream &core_file) const {
+ core_file.write(m_auxv.data(), m_auxv.size());
+ }
+};
+#endif // NOTE_HPP__
--- /dev/null
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#ifndef PROGRAM_HPP__
+#define PROGRAM_HPP__
+
+#include "note.hpp"
+
+#include <vector>
+
+template <typename T>
+class ProgramTableEntryBase {
+ public:
+ typename T::ProgramTableHeader header;
+ explicit ProgramTableEntryBase(uint32_t p_type) {
+ memset(&header, 0, sizeof(header));
+ header.p_type = p_type;
+ }
+};
+
+template <typename T>
+class ProgramTableEntryNote : public ProgramTableEntryBase<T> {
+ public:
+ ProgramTableEntryNote() : ProgramTableEntryBase<T>(PT_NOTE) {
+ this->header.p_flags = PF_R;
+ this->header.p_align = 1;
+ }
+ std::vector<Note> notes;
+};
+
+template <typename T>
+class ProgramTableEntryLoad : public ProgramTableEntryBase<T> {
+ public:
+ ProgramTableEntryLoad() : ProgramTableEntryBase<T>(PT_LOAD) {
+ this->header.p_flags = PF_R;
+ }
+};
+
+#endif // PROGRAM_HPP__
return (string_len >= suffix_len) && !strcmp(string + string_len - suffix_len, suffix);
}
+bool file_exists(const char *path)
+{
+ struct stat buf;
+ return stat(path, &buf) == 0;
+}
+
/**
* @}
*/
bool string_ends_with(const char *string, const char *suffix);
+bool file_exists(const char *path);
+
#ifdef __cplusplus
}
#endif
configure_test("log_dump_normal")
configure_test("log_dump_crash_root_path")
configure_test("dump_systemstate_extras")
+configure_test("livedumper")
get_property(TESTS_LIST GLOBAL PROPERTY TMP_TESTS_LIST)
--- /dev/null
+#!/bin/bash
+
+# Custom report path test
+
+if [ -z "${CRASH_WORKER_SYSTEM_TESTS}" ]; then
+ CRASH_WORKER_SYSTEM_TESTS="@CRASH_SYSTEM_TESTS_PATH@"
+fi
+
+. ${CRASH_WORKER_SYSTEM_TESTS}/utils/minicore-utils.sh
+
+mount -o rw,remount /
+
+clean_crash_dump
+
+{
+ ${CRASH_WORKER_SYSTEM_TESTS}/utils/kenny &
+ sleep 1
+ CRASH_MANAGER_OUTPUT=`crash-manager -p $! -t 555 -l -r | grep -e "^REPORT_PATH="`
+} 1> /dev/null 2>&1
+
+sleep 2
+
+echo "OUTPUT: "
+REPORT_PATH=`echo ${CRASH_MANAGER_OUTPUT} | cut -c 13-`
+
+wait_for_app crash-manager
+
+pushd ${CRASH_DUMP_PATH}
+
+if [ ! -f ${REPORT_PATH} ]; then
+ exit_with_code "Report path does not exist" 1
+fi
+
+if ! unzip ${REPORT_PATH} > /dev/null; then
+ popd
+ exit_with_code "FAIL: report not found in ${CRASH_DUMP_PATH}" 1
+fi
+
+COREDUMP=`find -name "kenny*coredump*"`
+if [[ ${COREDUMP} =~ .*tar ]]; then
+ untar_file ${COREDUMP}
+ COREDUMP=${COREDUMP%.tar}
+fi
+
+RESULT=`gdb ${CRASH_WORKER_SYSTEM_TESTS}/utils/kenny ${COREDUMP} --batch -ex "thread apply all bt"`
+popd
+
+check "MAGICNAME.*id=.*kenny.cpp:31"
+check "run.*id=.*kenny.cpp:56"
+
+if [[ $(pidof kenny) ]]; then
+ kill -9 `pidof kenny`
+else
+ exit_with_code "FAIL" 1
+fi
+
+exit_with_code "SUCCESS" 0