mirror of
https://github.com/projectacrn/acrn-hypervisor.git
synced 2025-06-19 20:22:46 +00:00
tools: acrn-crashlog: add APIs for client and debugger
This patch is the initial patch for some APIs for client and debugger. Usercrash works as C/S model. The usercrash_c runs as the client of usercrash. File crash_dump.c provides the APIs for client and debugger to dump some crash information. Signed-off-by: xiaojin2 <xiaojing.liu@intel.com> Reviewed-by: Zhang Yanmin <yanmin.zhang@intel.com> Reviewed-by: Liu Chuansheng <chuansheng.liu@intel.com> Reviewed-by: Zhao Yakui <yakui.zhao@intel.com> Reviewed-by: Geoffroy Van Cutsem <geoffroy.vancutsem@intel.com> Acked-by: Eddie Dong <Eddie.dong@intel.com>
This commit is contained in:
parent
2ccb5e3887
commit
afe1a64948
@ -12,7 +12,11 @@ usercrash_s: $(BUILDDIR)/usercrash/obj/protocol.o \
|
||||
|
||||
usercrash_c: $(BUILDDIR)/usercrash/obj/protocol.o \
|
||||
$(BUILDDIR)/usercrash/obj/client.o \
|
||||
$(BUILDDIR)/common/obj/log_sys.o
|
||||
$(BUILDDIR)/usercrash/obj/crash_dump.o \
|
||||
$(BUILDDIR)/common/obj/log_sys.o \
|
||||
$(BUILDDIR)/common/obj/cmdutils.o \
|
||||
$(BUILDDIR)/common/obj/fsutils.o \
|
||||
$(BUILDDIR)/common/obj/strutils.o
|
||||
$(CC) -g $(CFLAGS) $(INCLUDE) $^ -o $(BUILDDIR)/usercrash/bin/$@ -lsystemd
|
||||
|
||||
$(BUILDDIR)/usercrash/obj/%.o:%.c
|
||||
|
314
tools/acrn-crashlog/usercrash/crash_dump.c
Normal file
314
tools/acrn-crashlog/usercrash/crash_dump.c
Normal file
@ -0,0 +1,314 @@
|
||||
/*
|
||||
* Copyright (C) <2018> Intel Corporation
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <signal.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <stdarg.h>
|
||||
#include "log_sys.h"
|
||||
#include "crash_dump.h"
|
||||
#include "cmdutils.h"
|
||||
#include "fsutils.h"
|
||||
#include "strutils.h"
|
||||
|
||||
#define GET_TID "/proc/%d/status"
|
||||
#define GET_COMM "/proc/%d/exe"
|
||||
#define GET_K_STACK "/proc/%d/stack"
|
||||
#define GET_MAPS "/proc/%d/maps"
|
||||
#define GET_OPEN_FILES "/proc/%d/fd"
|
||||
#define DEBUGGER_SIGNAL (__SIGRTMIN + 3)
|
||||
#define DUMP_FILE "/tmp/core"
|
||||
#define BUFFER_SIZE 8196
|
||||
#define LINK_LEN 512
|
||||
|
||||
static void loginfo(int fd, const char *fmt, ...)
|
||||
{
|
||||
char buf[512];
|
||||
va_list ap;
|
||||
size_t len;
|
||||
|
||||
va_start(ap, fmt);
|
||||
vsnprintf(buf, sizeof(buf), fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
len = strlen(buf);
|
||||
if (len <= 0)
|
||||
return;
|
||||
|
||||
write(fd, buf, len);
|
||||
}
|
||||
|
||||
static const char *get_signame(int sig)
|
||||
{
|
||||
switch (sig) {
|
||||
case SIGABRT:
|
||||
return "SIGABRT";
|
||||
case SIGBUS:
|
||||
return "SIGBUS";
|
||||
case SIGFPE:
|
||||
return "SIGFPE";
|
||||
case SIGILL:
|
||||
return "SIGILL";
|
||||
case SIGSEGV:
|
||||
return "SIGSEGV";
|
||||
#if defined(SIGSTKFLT)
|
||||
case SIGSTKFLT:
|
||||
return "SIGSTKFLT";
|
||||
#endif
|
||||
case SIGSTOP:
|
||||
return "SIGSTOP";
|
||||
case SIGSYS:
|
||||
return "SIGSYS";
|
||||
case SIGTRAP:
|
||||
return "SIGTRAP";
|
||||
case DEBUGGER_SIGNAL:
|
||||
return "<debuggerd signal>";
|
||||
default:
|
||||
return "?";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Save core dump to file.
|
||||
* @filename: core dump file name
|
||||
* return 0 on success, or -1 on error
|
||||
*/
|
||||
static int save_coredump(const char *filename)
|
||||
{
|
||||
size_t read;
|
||||
char input_buffer[BUFFER_SIZE];
|
||||
FILE *dump_file;
|
||||
|
||||
/* open dump file in write and binary mode */
|
||||
dump_file = fopen(filename, "wb");
|
||||
if (dump_file != NULL) {
|
||||
/* Read from STDIN and write to dump file */
|
||||
while (1) {
|
||||
read = fread(input_buffer, 1, BUFFER_SIZE, stdin);
|
||||
if (read > 0) {
|
||||
if (fwrite(input_buffer, 1, read, dump_file) <=
|
||||
0) {
|
||||
LOGE("fwrite error\n");
|
||||
goto fail;
|
||||
}
|
||||
continue;
|
||||
} else if (read == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
LOGE("fread error\n");
|
||||
goto fail;
|
||||
}
|
||||
fclose(dump_file);
|
||||
return 0;
|
||||
}
|
||||
|
||||
LOGE("fopen core file failed\n");
|
||||
return -1;
|
||||
|
||||
fail:
|
||||
fclose(dump_file);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int get_backtrace(int pid, int fd, int sig, char *comm)
|
||||
{
|
||||
char *membkt;
|
||||
char format[512];
|
||||
|
||||
loginfo(fd, "\nBackTrace:\n\n");
|
||||
memset(format, 0, sizeof(format));
|
||||
if (sig == DEBUGGER_SIGNAL) {
|
||||
snprintf(format, sizeof(format), "-p %d", pid);
|
||||
} else {
|
||||
snprintf(format, sizeof(format), "%s %s", comm, DUMP_FILE);
|
||||
if (save_coredump(DUMP_FILE) == -1) {
|
||||
LOGE("save core file failed\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
membkt = exec_out2mem(GET_GDB_INFO, format);
|
||||
if (!membkt) {
|
||||
LOGE("get gdb info failed\n");
|
||||
return -1;
|
||||
}
|
||||
write(fd, membkt, strlen(membkt));
|
||||
free(membkt);
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Save process proc info to file.
|
||||
* @pid: process pid
|
||||
* @fd: usercrash file fd
|
||||
* @path: process proc path
|
||||
* @name: a string save to file
|
||||
* return 0 on success, or -1 on error
|
||||
*/
|
||||
static int save_proc_info(int pid, int fd, char *path, char *name)
|
||||
{
|
||||
int ret;
|
||||
char *data;
|
||||
char format[128];
|
||||
unsigned long size;
|
||||
|
||||
loginfo(fd, "\n%s:\n\n", name);
|
||||
memset(format, 0, sizeof(format));
|
||||
snprintf(format, sizeof(format), path, pid);
|
||||
ret = read_file(format, &size, (void *)&data);
|
||||
if (ret) {
|
||||
LOGE("read file failed\n");
|
||||
return -1;
|
||||
}
|
||||
write(fd, data, size);
|
||||
free(data);
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
static int get_openfiles(int pid, int fd, char *path, char *name)
|
||||
{
|
||||
int ret;
|
||||
int loop;
|
||||
int fdcount;
|
||||
char *fdname;
|
||||
char format[128];
|
||||
char *files[256];
|
||||
char linkname[LINK_LEN];
|
||||
|
||||
loginfo(fd, "\n%s:\n\n", name);
|
||||
memset(format, 0, sizeof(format));
|
||||
snprintf(format, sizeof(format), path, pid);
|
||||
fdcount = lsdir(format, files, ARRAY_SIZE(files));
|
||||
if (fdcount < 0) {
|
||||
LOGE("get fd list failed\n");
|
||||
return -1;
|
||||
}
|
||||
for (loop = 2; loop < fdcount; loop++) {
|
||||
memset(linkname, 0, LINK_LEN);
|
||||
ret = readlink(files[loop], linkname, LINK_LEN);
|
||||
if (ret < 0 || ret >= LINK_LEN) {
|
||||
LOGE("get fd link fd failed\n");
|
||||
continue;
|
||||
}
|
||||
fdname = strrchr(files[loop], '/') + 1;
|
||||
loginfo(fd, "%s -> %s\n", fdname, linkname);
|
||||
}
|
||||
for (loop = 0; loop < fdcount; loop++)
|
||||
free(files[loop]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int save_usercrash_file(int pid, int tgid, char *comm,
|
||||
int sig, int out_fd)
|
||||
{
|
||||
int ret;
|
||||
|
||||
loginfo(out_fd, "*** *** *** *** *** *** *** *** *** *** *** *** *** ");
|
||||
loginfo(out_fd, "*** *** ***\n");
|
||||
loginfo(out_fd, "pid: %d, tgid: %d, comm: %s\n\n\n", pid, tgid, comm);
|
||||
loginfo(out_fd, "signal: %d (%s)\n", sig, get_signame(sig));
|
||||
|
||||
ret = save_proc_info(pid, out_fd, GET_K_STACK, "Stack");
|
||||
if (ret) {
|
||||
LOGE("save stack failed\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = save_proc_info(pid, out_fd, GET_MAPS, "Maps");
|
||||
if (ret) {
|
||||
LOGE("save maps failed\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = get_openfiles(pid, out_fd, GET_OPEN_FILES, "Open files");
|
||||
if (ret) {
|
||||
LOGE("save openfiles failed\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = get_backtrace(pid, out_fd, sig, comm);
|
||||
if (ret) {
|
||||
LOGE("save backtrace failed\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_key_value(int pid, char *path, char *key, char *value)
|
||||
{
|
||||
int len;
|
||||
int ret;
|
||||
unsigned long size;
|
||||
char *data;
|
||||
char *msg;
|
||||
char *start;
|
||||
char *end;
|
||||
char format[128];
|
||||
|
||||
memset(format, 0, sizeof(format));
|
||||
snprintf(format, sizeof(format), path, pid);
|
||||
ret = read_file(format, &size, (void *)&data);
|
||||
if (ret) {
|
||||
LOGE("read file failed\n");
|
||||
return -1;
|
||||
}
|
||||
msg = strstr(data, key);
|
||||
if (!msg) {
|
||||
LOGE("find key:%s failed\n", key);
|
||||
free(data);
|
||||
return -1;
|
||||
}
|
||||
end = strchr(msg, '\n');
|
||||
if (end == NULL)
|
||||
end = data + size;
|
||||
start = msg + strlen(key);
|
||||
len = end - start;
|
||||
memcpy(value, start, len);
|
||||
*(value + len) = 0;
|
||||
free(data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get and save process info to out_fd.
|
||||
* @pid: process pid
|
||||
* @sig: signal from core dump
|
||||
* @out_fd: file fd to save info
|
||||
*/
|
||||
void crash_dump(int pid, int sig, int out_fd)
|
||||
{
|
||||
int tgid;
|
||||
int ret;
|
||||
char comm[LINK_LEN];
|
||||
char result[16];
|
||||
char format[128];
|
||||
|
||||
memset(format, 0, sizeof(format));
|
||||
snprintf(format, sizeof(format), GET_COMM, pid);
|
||||
ret = readlink(format, comm, LINK_LEN);
|
||||
if (ret < 0 || ret >= LINK_LEN) {
|
||||
LOGE("get process exe link failed\n");
|
||||
return;
|
||||
}
|
||||
comm[ret] = '\0';
|
||||
|
||||
ret = get_key_value(pid, GET_TID, "Tgid:", result);
|
||||
if (ret) {
|
||||
LOGE("get Tgid error\n");
|
||||
return;
|
||||
}
|
||||
tgid = atoi(result);
|
||||
if (!sig)
|
||||
sig = DEBUGGER_SIGNAL;
|
||||
if (save_usercrash_file(pid, tgid, comm, sig, out_fd))
|
||||
LOGE("dump log file failed\n");
|
||||
}
|
52
tools/acrn-crashlog/usercrash/include/crash_dump.h
Normal file
52
tools/acrn-crashlog/usercrash/include/crash_dump.h
Normal file
@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright (C) <2018> Intel Corporation
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#ifndef CRASH_DUMP_H
|
||||
#define CRASH_DUMP_H
|
||||
|
||||
/**
|
||||
* So far, crash_dump use gdb command to dump process info. But in the future,
|
||||
* we will replace gdb command with a better implementation.
|
||||
*/
|
||||
#define GET_GDB_INFO "/usr/bin/gdb %s -batch "\
|
||||
"-ex 'bt' "\
|
||||
"-ex 'printf \"\nRegisters\n\"' "\
|
||||
"-ex 'info registers' "\
|
||||
"-ex 'printf \"\n\nMemory near rax\n\"' "\
|
||||
"-ex 'x/16x $rax-0x20' "\
|
||||
"-ex 'x/48x $rax' "\
|
||||
"-ex 'printf \"\n\nMemory near rbx\n\"' "\
|
||||
"-ex 'x/16x $rbx-0x20' "\
|
||||
"-ex 'x/48x $rbx' "\
|
||||
"-ex 'printf \"\n\nMemory near rcx\n\"' "\
|
||||
"-ex 'x/16x $rcx-0x20' "\
|
||||
"-ex 'x/48x $rcx' "\
|
||||
"-ex 'printf \"\n\nMemory near rdx\n\"' "\
|
||||
"-ex 'x/16x $rdx-0x20' "\
|
||||
"-ex 'x/48x $rdx' "\
|
||||
"-ex 'printf \"\n\nMemory near rsi\n\"' "\
|
||||
"-ex 'x/16x $rsi-0x20' "\
|
||||
"-ex 'x/48x $rsi' "\
|
||||
"-ex 'printf \"\n\nMemory near rdi\n\"' "\
|
||||
"-ex 'x/16x $rdi-0x20' "\
|
||||
"-ex 'x/48x $rdi' "\
|
||||
"-ex 'printf \"\n\nMemory near rbp\n\"' "\
|
||||
"-ex 'x/16x $rbp-0x20' "\
|
||||
"-ex 'x/48x $rbp' "\
|
||||
"-ex 'printf \"\n\nMemory near rsp\n\"' "\
|
||||
"-ex 'x/16x $rsp-0x20' "\
|
||||
"-ex 'x/48x $rsp' "\
|
||||
"-ex 'printf \"\n\ncode around rip\n\"' "\
|
||||
"-ex 'x/8i $rip-0x20' "\
|
||||
"-ex 'x/48i $rip' "\
|
||||
"-ex 'printf \"\nThreads\n\n\"' "\
|
||||
"-ex 'info threads' "\
|
||||
"-ex 'printf \"\nThreads backtrace\n\n\"' "\
|
||||
"-ex 'thread apply all bt' "\
|
||||
"-ex 'quit'"
|
||||
|
||||
void crash_dump(int pid, int sig, int out_fd);
|
||||
|
||||
#endif
|
32
tools/acrn-crashlog/usercrash/include/packet.h
Normal file
32
tools/acrn-crashlog/usercrash/include/packet.h
Normal file
@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright (C) <2018> Intel Corporation
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#ifndef CLIENT_H
|
||||
#define CLIENT_H
|
||||
|
||||
#define COMM_NAME_LEN 64
|
||||
#define SOCKET_NAME "user_crash"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
enum CrashPacketType {
|
||||
/* Initial request from crash_dump */
|
||||
kDumpRequest = 0,
|
||||
|
||||
/* Notification of a completed crash dump */
|
||||
kCompletedDump,
|
||||
|
||||
/* Responses to kRequest */
|
||||
kPerformDump
|
||||
};
|
||||
|
||||
struct crash_packet {
|
||||
enum CrashPacketType packet_type;
|
||||
int pid;
|
||||
char name[COMM_NAME_LEN];
|
||||
};
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue
Block a user