blob: c537a7ff05ff9ce4e30912d75067654c89c1076f [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/wait.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <assert.h>
#include <ctype.h>
#include "port_dso.h"
#include "port_malloc.h"
#include "stack_dump.h"
#if defined(MACOSX)
#include <crt_externs.h>
#define environ (*_NSGetEnviron())
#else
extern char** environ;
#endif
static char* g_curdir = NULL;
static char* g_cmdline = NULL;
static inline native_segment_t* sd_find_segment(native_module_t* module, void* ip)
{
for (size_t i = 0; i < module->seg_count; i++)
{
if (module->segments[i].base <= ip &&
(char*)module->segments[i].base + module->segments[i].size > ip)
return &module->segments[i];
}
assert(0);
return NULL;
}
void sd_get_c_method_info(CFunInfo* info, native_module_t* module, void* ip)
{
*info->name = '\0';
*info->filename = '\0';
info->line = -1;
if (!module || !module->filename)
return;
POINTER_SIZE_INT offset = (POINTER_SIZE_INT)ip;
if (strstr(module->filename, PORT_DSO_EXT) != NULL) // Shared object
{ // IP for addr2line should be an offset within shared library
native_segment_t* seg = sd_find_segment(module, ip);
offset -= (POINTER_SIZE_INT)seg->base;
}
int po[2];
pipe(po);
char ip_str[20];
sprintf(ip_str, "0x%"PI_FMT"x\n", offset);
if (!fork())
{
close(po[0]);
dup2(po[1], 1);
execlp("addr2line", "addr2line", "-f", "-s", "-e", module->filename, "-C", ip_str, NULL);
//fprintf(stderr, "Warning: Cannot run addr2line. No symbolic information will be available\n");
printf("??\n??:0\n"); // Emulate addr2line output
exit(-1);
}
else
{
close(po[1]);
char buf[sizeof(info->name) + sizeof(info->filename)];
int status;
wait(&status);
int count = read(po[0], buf, sizeof(buf) - 1);
close(po[0]);
if (count < 0)
{
fprintf(stderr, "read() failed during addr2line execution\n");
return;
}
while (isspace(buf[count-1]))
count--;
buf[count] = '\0';
int i = 0;
for (; i < count; i++)
{
if (buf[i] == '\n')
{ // Function name is limited by '\n'
buf[i] = '\0';
strncpy(info->name, buf, sizeof(info->name));
break;
}
}
if (i == count)
return;
char* fn = buf + i + 1;
for (; i < count && buf[i] != ':'; i++); // File name and line number are separated by ':'
if (i == count)
return;
buf[i] = '\0';
strncpy(info->filename, fn, sizeof(info->filename));
info->line = atoi(buf + i + 1); // Line number
if (info->line == 0)
info->line = -1;
}
}
void sd_init_crash_handler()
{
// Get current directory
char buf[PATH_MAX + 1];
char* cwd = getcwd(buf, sizeof(buf));
if (cwd)
{
cwd = (char*)STD_MALLOC(strlen(cwd) + 1);
g_curdir = cwd;
if (cwd)
strcpy(cwd, buf);
}
// Get command line
sprintf(buf, "/proc/%d/cmdline", getpid());
int file = open(buf, O_RDONLY);
if (file > 0)
{
size_t size = 0;
char rdbuf[256];
ssize_t rd;
do
{
rd = read(file, rdbuf, sizeof(rdbuf));
size += (rd > 0) ? rd : 0;
} while (rd == sizeof(rdbuf));
if (size)
{
char* cmd = (char*)STD_MALLOC(size + 1);
g_cmdline = cmd;
if (cmd)
{
cmd[size] = '\0';
lseek(file, 0, SEEK_SET);
read(file, cmd, size);
}
}
close(file);
}
}
void sd_cleanup_crash_handler()
{
if (g_curdir)
STD_FREE(g_curdir);
if (g_cmdline)
STD_FREE(g_cmdline);
}
void sd_print_cmdline_cwd()
{
fprintf(stderr, "\nCommand line:\n");
if (g_cmdline)
{
for (const char* ptr = g_cmdline; *ptr; ptr += strlen(ptr) + 1)
fprintf(stderr, "%s ", ptr);
fprintf(stderr, "\n");
}
else
fprintf(stderr, "is unavailable, probably /proc is not mounted\n");
fprintf(stderr, "\nWorking directory:\n%s\n", g_curdir ? g_curdir : "'null'");
}
void sd_print_environment()
{
fprintf(stderr, "\nEnvironment variables:\n");
for (char** env = environ; *env; ++env)
fprintf(stderr, "%s\n", *env);
}