2014-03-02 00:35:30 +00:00
|
|
|
/**
|
|
|
|
* Copyright (c) 2014, Timothy Stack
|
|
|
|
*
|
|
|
|
* All rights reserved.
|
|
|
|
*
|
|
|
|
* Redistribution and use in source and binary forms, with or without
|
|
|
|
* modification, are permitted provided that the following conditions are met:
|
|
|
|
*
|
|
|
|
* * Redistributions of source code must retain the above copyright notice, this
|
|
|
|
* list of conditions and the following disclaimer.
|
|
|
|
* * Redistributions in binary form must reproduce the above copyright notice,
|
|
|
|
* this list of conditions and the following disclaimer in the documentation
|
|
|
|
* and/or other materials provided with the distribution.
|
|
|
|
* * Neither the name of Timothy Stack nor the names of its contributors
|
|
|
|
* may be used to endorse or promote products derived from this software
|
|
|
|
* without specific prior written permission.
|
|
|
|
*
|
|
|
|
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY
|
|
|
|
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
|
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
|
|
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
|
|
|
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
|
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
|
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
|
|
|
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
|
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
*
|
|
|
|
* @file lnav_log.cc
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "config.h"
|
|
|
|
|
2014-03-02 03:42:59 +00:00
|
|
|
#include <time.h>
|
2014-03-06 14:58:49 +00:00
|
|
|
#include <ctype.h>
|
2014-03-02 00:35:30 +00:00
|
|
|
#include <fcntl.h>
|
2014-03-09 15:54:55 +00:00
|
|
|
#include <stdio.h>
|
2014-03-17 11:17:17 +00:00
|
|
|
#include <assert.h>
|
2014-03-02 00:35:30 +00:00
|
|
|
#include <stdarg.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <signal.h>
|
|
|
|
#include <libgen.h>
|
2014-03-18 10:37:00 +00:00
|
|
|
|
|
|
|
#ifdef HAVE_EXECINFO_H
|
2014-03-02 00:35:30 +00:00
|
|
|
#include <execinfo.h>
|
2014-03-18 10:37:00 +00:00
|
|
|
#endif
|
|
|
|
|
2014-03-02 00:35:30 +00:00
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/time.h>
|
|
|
|
#include <sys/utsname.h>
|
2014-03-06 15:16:43 +00:00
|
|
|
#include <sys/wait.h>
|
2014-10-20 05:19:40 +00:00
|
|
|
#include <sys/param.h>
|
2014-03-18 10:41:08 +00:00
|
|
|
|
|
|
|
#if defined HAVE_NCURSESW_CURSES_H
|
|
|
|
# include <ncursesw/termcap.h>
|
|
|
|
#elif defined HAVE_NCURSESW_H
|
|
|
|
# include <termcap.h>
|
|
|
|
#elif defined HAVE_NCURSES_CURSES_H
|
|
|
|
# include <ncurses/termcap.h>
|
|
|
|
#elif defined HAVE_NCURSES_H
|
|
|
|
# include <termcap.h>
|
|
|
|
#elif defined HAVE_CURSES_H
|
|
|
|
# include <termcap.h>
|
|
|
|
#else
|
|
|
|
# error "SysV or X/Open-compatible Curses header file required"
|
|
|
|
#endif
|
2014-03-02 00:35:30 +00:00
|
|
|
|
|
|
|
#include "lnav_log.hh"
|
|
|
|
|
|
|
|
static const size_t BUFFER_SIZE = 256 * 1024;
|
|
|
|
static const size_t MAX_LOG_LINE_SIZE = 2048;
|
|
|
|
|
|
|
|
static const char *CRASH_MSG =
|
|
|
|
"\n"
|
|
|
|
"\n"
|
|
|
|
"==== GURU MEDITATION ====\n"
|
|
|
|
"Unfortunately, lnav has crashed, sorry for the inconvenience.\n"
|
|
|
|
"\n"
|
|
|
|
"You can help improve lnav by sending the following file to " PACKAGE_BUGREPORT " :\n"
|
|
|
|
" %s\n"
|
|
|
|
"=========================\n";
|
|
|
|
|
|
|
|
FILE *lnav_log_file;
|
|
|
|
lnav_log_level_t lnav_log_level;
|
|
|
|
const char *lnav_log_crash_dir;
|
|
|
|
const struct termios *lnav_log_orig_termios;
|
|
|
|
|
|
|
|
static struct {
|
|
|
|
size_t lr_length;
|
|
|
|
off_t lr_frag_start;
|
|
|
|
off_t lr_frag_end;
|
|
|
|
char lr_data[BUFFER_SIZE];
|
|
|
|
} log_ring = {
|
|
|
|
0,
|
|
|
|
BUFFER_SIZE,
|
|
|
|
0,
|
|
|
|
};
|
|
|
|
|
|
|
|
static const char *LEVEL_NAMES[] = {
|
|
|
|
"D",
|
|
|
|
"I",
|
|
|
|
"W",
|
|
|
|
"E",
|
|
|
|
};
|
|
|
|
|
|
|
|
static char *log_alloc(void)
|
|
|
|
{
|
|
|
|
off_t data_end = log_ring.lr_length + MAX_LOG_LINE_SIZE;
|
|
|
|
|
|
|
|
if (data_end >= BUFFER_SIZE) {
|
|
|
|
const char *new_start = &log_ring.lr_data[MAX_LOG_LINE_SIZE];
|
|
|
|
|
|
|
|
new_start = (const char *)memchr(
|
|
|
|
new_start, '\n', log_ring.lr_length - MAX_LOG_LINE_SIZE);
|
|
|
|
log_ring.lr_frag_start = new_start - log_ring.lr_data;
|
|
|
|
log_ring.lr_frag_end = log_ring.lr_length;
|
|
|
|
log_ring.lr_length = 0;
|
2014-03-17 11:17:17 +00:00
|
|
|
|
|
|
|
assert(log_ring.lr_frag_start >= 0);
|
|
|
|
assert(log_ring.lr_frag_start <= BUFFER_SIZE);
|
2014-03-02 00:35:30 +00:00
|
|
|
} else if (data_end >= log_ring.lr_frag_start) {
|
|
|
|
const char *new_start = &log_ring.lr_data[log_ring.lr_frag_start];
|
|
|
|
|
|
|
|
new_start = (const char *)memchr(
|
2014-03-17 11:17:17 +00:00
|
|
|
new_start, '\n', log_ring.lr_frag_end - log_ring.lr_frag_start);
|
|
|
|
assert(new_start != NULL);
|
2014-03-02 00:35:30 +00:00
|
|
|
log_ring.lr_frag_start = new_start - log_ring.lr_data;
|
2014-03-17 11:17:17 +00:00
|
|
|
assert(log_ring.lr_frag_start >= 0);
|
|
|
|
assert(log_ring.lr_frag_start <= BUFFER_SIZE);
|
2014-03-02 00:35:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return &log_ring.lr_data[log_ring.lr_length];
|
|
|
|
}
|
|
|
|
|
2014-05-05 13:44:58 +00:00
|
|
|
void log_argv(int argc, char *argv[])
|
|
|
|
{
|
|
|
|
const char *log_path = getenv("LNAV_LOG_PATH");
|
|
|
|
|
|
|
|
if (log_path != NULL) {
|
|
|
|
lnav_log_file = fopen(log_path, "a");
|
|
|
|
}
|
|
|
|
|
|
|
|
log_info("argv[%d] =", argc);
|
|
|
|
for (int lpc = 0; lpc < argc; lpc++) {
|
|
|
|
log_info(" [%d] = %s", lpc, argv[lpc]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-03-02 00:35:30 +00:00
|
|
|
void log_host_info(void)
|
|
|
|
{
|
2014-10-20 05:16:40 +00:00
|
|
|
char cwd[MAXPATHLEN];
|
2014-03-02 00:35:30 +00:00
|
|
|
struct utsname un;
|
|
|
|
|
|
|
|
uname(&un);
|
|
|
|
log_info("uname:")
|
|
|
|
log_info(" sysname=%s", un.sysname);
|
|
|
|
log_info(" nodename=%s", un.nodename);
|
|
|
|
log_info(" machine=%s", un.machine);
|
|
|
|
log_info(" release=%s", un.release);
|
|
|
|
log_info(" version=%s", un.version);
|
|
|
|
log_info("Environment:");
|
2014-10-20 05:16:40 +00:00
|
|
|
log_info(" HOME=%s", getenv("HOME"));
|
|
|
|
log_info(" LANG=%s", getenv("LANG"));
|
|
|
|
log_info(" PATH=%s", getenv("PATH"));
|
2014-03-02 00:35:30 +00:00
|
|
|
log_info(" TERM=%s", getenv("TERM"));
|
2014-10-20 05:16:40 +00:00
|
|
|
log_info(" TZ=%s", getenv("TZ"));
|
|
|
|
log_info("Process:")
|
|
|
|
log_info(" pid=%d", getpid());
|
|
|
|
log_info(" ppid=%d", getppid());
|
|
|
|
log_info(" pgrp=%d", getpgrp());
|
|
|
|
log_info(" uid=%d", getuid());
|
|
|
|
log_info(" gid=%d", getgid());
|
|
|
|
log_info(" euid=%d", geteuid());
|
|
|
|
log_info(" egid=%d", getegid());
|
|
|
|
getcwd(cwd, sizeof(cwd));
|
|
|
|
log_info(" cwd=%s", cwd);
|
2014-03-02 00:35:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void log_msg(lnav_log_level_t level, const char *src_file, int line_number,
|
|
|
|
const char *fmt, ...)
|
|
|
|
{
|
|
|
|
struct timeval curr_time;
|
|
|
|
struct tm localtm;
|
2014-10-20 05:16:40 +00:00
|
|
|
ssize_t prefix_size;
|
2014-03-02 00:35:30 +00:00
|
|
|
va_list args;
|
|
|
|
char *line;
|
2014-10-20 05:16:40 +00:00
|
|
|
ssize_t rc;
|
2014-03-02 00:35:30 +00:00
|
|
|
|
|
|
|
if (level < lnav_log_level) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
va_start(args, fmt);
|
|
|
|
gettimeofday(&curr_time, NULL);
|
|
|
|
localtime_r(&curr_time.tv_sec, &localtm);
|
|
|
|
line = log_alloc();
|
|
|
|
prefix_size = snprintf(
|
|
|
|
line, MAX_LOG_LINE_SIZE,
|
|
|
|
"%4d-%02d-%02dT%02d:%02d:%02d.%03d %s %s:%d ",
|
|
|
|
localtm.tm_year + 1900,
|
|
|
|
localtm.tm_mon + 1,
|
|
|
|
localtm.tm_mday,
|
|
|
|
localtm.tm_hour,
|
|
|
|
localtm.tm_min,
|
|
|
|
localtm.tm_sec,
|
|
|
|
curr_time.tv_usec / 1000,
|
|
|
|
LEVEL_NAMES[level],
|
|
|
|
basename((char *)src_file),
|
|
|
|
line_number);
|
|
|
|
rc = vsnprintf(&line[prefix_size], MAX_LOG_LINE_SIZE - prefix_size,
|
|
|
|
fmt, args);
|
|
|
|
if (rc >= (MAX_LOG_LINE_SIZE - prefix_size)) {
|
|
|
|
rc = MAX_LOG_LINE_SIZE - prefix_size - 1;
|
|
|
|
}
|
|
|
|
line[prefix_size + rc] = '\n';
|
|
|
|
log_ring.lr_length += prefix_size + rc + 1;
|
|
|
|
if (lnav_log_file != NULL) {
|
|
|
|
fwrite(line, 1, prefix_size + rc + 1, lnav_log_file);
|
|
|
|
fflush(lnav_log_file);
|
|
|
|
}
|
|
|
|
va_end(args);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void sigabrt(int sig)
|
|
|
|
{
|
|
|
|
char crash_path[1024], latest_crash_path[1024];
|
|
|
|
int fd, frame_count;
|
|
|
|
void *frames[128];
|
|
|
|
struct tm localtm;
|
|
|
|
time_t curr_time;
|
|
|
|
|
|
|
|
if (lnav_log_crash_dir == NULL) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
log_error("Received signal: %d", sig);
|
|
|
|
|
2014-03-18 10:37:00 +00:00
|
|
|
#ifdef HAVE_EXECINFO_H
|
2014-03-02 00:35:30 +00:00
|
|
|
frame_count = backtrace(frames, 128);
|
2014-03-18 10:37:00 +00:00
|
|
|
#endif
|
2014-03-02 00:35:30 +00:00
|
|
|
curr_time = time(NULL);
|
|
|
|
localtime_r(&curr_time, &localtm);
|
|
|
|
snprintf(crash_path, sizeof(crash_path),
|
|
|
|
"%s/crash-%4d-%02d-%02d-%02d-%02d-%02d.%d.log",
|
|
|
|
lnav_log_crash_dir,
|
|
|
|
localtm.tm_year + 1900,
|
|
|
|
localtm.tm_mon + 1,
|
|
|
|
localtm.tm_mday,
|
|
|
|
localtm.tm_hour,
|
|
|
|
localtm.tm_min,
|
|
|
|
localtm.tm_sec,
|
|
|
|
getpid());
|
|
|
|
snprintf(latest_crash_path, sizeof(latest_crash_path),
|
|
|
|
"%s/latest-crash.log", lnav_log_crash_dir);
|
|
|
|
if ((fd = open(crash_path, O_CREAT|O_TRUNC|O_WRONLY, 0600)) != -1) {
|
|
|
|
if (log_ring.lr_frag_start < BUFFER_SIZE) {
|
|
|
|
write(fd, &log_ring.lr_data[log_ring.lr_frag_start],
|
|
|
|
log_ring.lr_frag_end - log_ring.lr_frag_start);
|
|
|
|
}
|
|
|
|
write(fd, log_ring.lr_data, log_ring.lr_length);
|
2014-03-18 10:37:00 +00:00
|
|
|
#ifdef HAVE_EXECINFO_H
|
2014-03-02 00:35:30 +00:00
|
|
|
backtrace_symbols_fd(frames, frame_count, fd);
|
2014-03-18 10:37:00 +00:00
|
|
|
#endif
|
2014-03-02 00:35:30 +00:00
|
|
|
close(fd);
|
|
|
|
|
|
|
|
remove(latest_crash_path);
|
|
|
|
symlink(crash_path, latest_crash_path);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (lnav_log_orig_termios != NULL) {
|
|
|
|
tcsetattr(STDOUT_FILENO, TCSAFLUSH, lnav_log_orig_termios);
|
|
|
|
}
|
|
|
|
fprintf(stderr, CRASH_MSG, crash_path);
|
2014-03-06 14:58:49 +00:00
|
|
|
|
2014-05-07 04:26:05 +00:00
|
|
|
#ifndef ATTACH_ON_SIGNAL
|
2014-03-06 14:58:49 +00:00
|
|
|
if (isatty(STDIN_FILENO)) {
|
|
|
|
char response;
|
|
|
|
|
|
|
|
fprintf(stderr, "\nWould you like to attach a debugger? (y/N) ");
|
|
|
|
fflush(stderr);
|
|
|
|
|
|
|
|
scanf("%c", &response);
|
|
|
|
|
|
|
|
if (tolower(response) == 'y') {
|
|
|
|
pid_t lnav_pid = getpid();
|
|
|
|
pid_t child_pid;
|
|
|
|
|
|
|
|
switch ((child_pid = fork())) {
|
|
|
|
case 0: {
|
|
|
|
char pid_str[32];
|
|
|
|
|
|
|
|
snprintf(pid_str, sizeof(pid_str), "--pid=%d", lnav_pid);
|
|
|
|
execlp("gdb", "gdb", pid_str, NULL);
|
|
|
|
|
|
|
|
snprintf(pid_str, sizeof(pid_str),
|
|
|
|
"--attach-pid=%d", lnav_pid);
|
|
|
|
execlp("lldb", "lldb", pid_str, NULL);
|
|
|
|
|
|
|
|
fprintf(stderr, "Could not attach gdb or lldb, exiting.\n");
|
|
|
|
_exit(1);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case -1: {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
default: {
|
|
|
|
int status;
|
|
|
|
|
|
|
|
while (wait(&status) < 0) {
|
|
|
|
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2014-03-30 01:57:12 +00:00
|
|
|
#endif
|
2014-03-06 14:58:49 +00:00
|
|
|
|
2014-03-02 00:35:30 +00:00
|
|
|
_exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
void log_install_handlers(void)
|
|
|
|
{
|
|
|
|
signal(SIGABRT, sigabrt);
|
|
|
|
signal(SIGSEGV, sigabrt);
|
|
|
|
signal(SIGBUS, sigabrt);
|
|
|
|
signal(SIGILL, sigabrt);
|
|
|
|
signal(SIGFPE, sigabrt);
|
|
|
|
}
|
2014-03-06 14:58:49 +00:00
|
|
|
|
|
|
|
void log_abort(void)
|
|
|
|
{
|
|
|
|
sigabrt(SIGABRT);
|
|
|
|
_exit(1);
|
|
|
|
}
|