91f6762edf
2007-07-14 Krister Walfridsson <cato@df.lth.se> * gnu/gcj/util/natGCInfo.cc: Use HAVE_STRERROR_R. * configure.ac: Remove check for strerror, add check for strerror_r. * configure: Regenerate. * include/config.h.in: Regenerate. From-SVN: r126660
459 lines
11 KiB
C++
459 lines
11 KiB
C++
/* natGCInfo.cc -- Native portion of support for creating heap dumps.
|
|
Copyright (C) 2007 Free Software Foundation
|
|
|
|
This file is part of libgcj.
|
|
|
|
This software is copyrighted work licensed under the terms of the
|
|
Libgcj License. Please consult the file "LIBGCJ_LICENSE" for
|
|
details. */
|
|
|
|
|
|
#include <config.h>
|
|
|
|
#include <gcj/cni.h>
|
|
|
|
#include <gnu/gcj/util/GCInfo.h>
|
|
|
|
#ifdef HAVE_PROC_SELF_MAPS
|
|
//
|
|
// If /proc/self/maps does not exist we assume we are doomed and do nothing.
|
|
//
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <fcntl.h>
|
|
#include <unistd.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
|
|
//
|
|
// Boehm GC includes.
|
|
//
|
|
#ifdef PACKAGE_NAME
|
|
#undef PACKAGE_NAME
|
|
#endif
|
|
|
|
#ifdef PACKAGE_STRING
|
|
#undef PACKAGE_STRING
|
|
#endif
|
|
|
|
#ifdef PACKAGE_TARNAME
|
|
#undef PACKAGE_TARNAME
|
|
#endif
|
|
|
|
#ifdef PACKAGE_VERSION
|
|
#undef PACKAGE_VERSION
|
|
#endif
|
|
|
|
#ifdef TRUE
|
|
#undef TRUE
|
|
#endif
|
|
|
|
#ifdef FALSE
|
|
#undef FALSE
|
|
#endif
|
|
|
|
extern "C" {
|
|
#include "private/dbg_mlc.h"
|
|
int GC_n_set_marks(hdr* hhdr);
|
|
ptr_t GC_clear_stack(ptr_t p);
|
|
extern int GC_gcj_kind;
|
|
extern int GC_gcj_debug_kind;
|
|
}
|
|
|
|
#endif
|
|
|
|
#ifdef HAVE_PROC_SELF_MAPS
|
|
|
|
static int gc_ok = 1;
|
|
|
|
struct gc_debug_info
|
|
{
|
|
int used;
|
|
int free;
|
|
int wasted;
|
|
int blocks;
|
|
FILE* fp;
|
|
};
|
|
|
|
static void
|
|
GC_print_debug_callback(hblk *h, word user_data)
|
|
{
|
|
hdr *hhdr = HDR(h);
|
|
size_t bytes = WORDS_TO_BYTES(hhdr -> hb_sz);
|
|
|
|
gc_debug_info *pinfo = (gc_debug_info *)user_data;
|
|
|
|
fprintf(pinfo->fp, "ptr = %#lx, kind = %d, size = %zd, marks = %d\n",
|
|
(unsigned long)h, hhdr->hb_obj_kind, bytes, GC_n_set_marks(hhdr));
|
|
}
|
|
|
|
/*
|
|
this next section of definitions shouldn't really be here.
|
|
copied from boehmgc/allchblk.c
|
|
*/
|
|
|
|
# define UNIQUE_THRESHOLD 32
|
|
# define HUGE_THRESHOLD 256
|
|
# define FL_COMPRESSION 8
|
|
# define N_HBLK_FLS (HUGE_THRESHOLD - UNIQUE_THRESHOLD)/FL_COMPRESSION \
|
|
+ UNIQUE_THRESHOLD
|
|
#ifndef USE_MUNMAP
|
|
extern "C" {
|
|
extern word GC_free_bytes[N_HBLK_FLS+1];
|
|
}
|
|
#endif
|
|
|
|
# ifdef USE_MUNMAP
|
|
# define IS_MAPPED(hhdr) (((hhdr) -> hb_flags & WAS_UNMAPPED) == 0)
|
|
# else /* !USE_MMAP */
|
|
# define IS_MAPPED(hhdr) 1
|
|
# endif /* USE_MUNMAP */
|
|
|
|
static void
|
|
GC_print_hblkfreelist_file(FILE *fp)
|
|
{
|
|
struct hblk * h;
|
|
word total_free = 0;
|
|
hdr * hhdr;
|
|
word sz;
|
|
int i;
|
|
|
|
fprintf(fp, "---------- Begin free map ----------\n");
|
|
for (i = 0; i <= N_HBLK_FLS; ++i)
|
|
{
|
|
h = GC_hblkfreelist[i];
|
|
#ifdef USE_MUNMAP
|
|
if (0 != h)
|
|
fprintf (fp, "Free list %ld:\n", (unsigned long)i);
|
|
#else
|
|
if (0 != h)
|
|
fprintf (fp, "Free list %ld (Total size %ld):\n",
|
|
(unsigned long)i,
|
|
(unsigned long)GC_free_bytes[i]);
|
|
#endif
|
|
while (h != 0)
|
|
{
|
|
hhdr = HDR(h);
|
|
sz = hhdr -> hb_sz;
|
|
fprintf (fp, "\t0x%lx size %lu ", (unsigned long)h,
|
|
(unsigned long)sz);
|
|
total_free += sz;
|
|
|
|
if (GC_is_black_listed (h, HBLKSIZE) != 0)
|
|
fprintf (fp, "start black listed\n");
|
|
else if (GC_is_black_listed(h, hhdr -> hb_sz) != 0)
|
|
fprintf (fp, "partially black listed\n");
|
|
else
|
|
fprintf (fp, "not black listed\n");
|
|
|
|
h = hhdr -> hb_next;
|
|
}
|
|
}
|
|
#ifndef USE_MUNMAP
|
|
if (total_free != GC_large_free_bytes)
|
|
{
|
|
fprintf (fp, "GC_large_free_bytes = %lu (INCONSISTENT!!)\n",
|
|
(unsigned long) GC_large_free_bytes);
|
|
}
|
|
#endif
|
|
fprintf (fp, "Total of %lu bytes on free list\n", (unsigned long)total_free);
|
|
fprintf (fp, "---------- End free map ----------\n");
|
|
}
|
|
|
|
static int GC_dump_count = 1;
|
|
|
|
static void
|
|
GC_print_debug_info_file(FILE* fp)
|
|
{
|
|
gc_debug_info info;
|
|
|
|
memset(&info, 0, sizeof info);
|
|
info.fp = fp;
|
|
|
|
if (gc_ok)
|
|
GC_gcollect();
|
|
fprintf(info.fp, "---------- Begin block map ----------\n");
|
|
GC_apply_to_all_blocks(GC_print_debug_callback, (word)(void*)(&info));
|
|
//fprintf(fp, "#Total used %d free %d wasted %d\n", info.used, info.free, info.wasted);
|
|
//fprintf(fp, "#Total blocks %d; %dK bytes\n", info.blocks, info.blocks*4);
|
|
fprintf(info.fp, "---------- End block map ----------\n");
|
|
|
|
//fprintf(fp, "\n***Free blocks:\n");
|
|
//GC_print_hblkfreelist();
|
|
}
|
|
|
|
namespace
|
|
{
|
|
class __attribute__ ((visibility ("hidden"))) GC_enumerator
|
|
{
|
|
public:
|
|
GC_enumerator(const char *name);
|
|
void enumerate();
|
|
private:
|
|
FILE* fp;
|
|
int bytes_fd;
|
|
|
|
void print_address_map();
|
|
void enumerate_callback(struct hblk *h);
|
|
static void enumerate_callback_adaptor(struct hblk *h, word dummy);
|
|
};
|
|
}
|
|
|
|
GC_enumerator::GC_enumerator(const char *name)
|
|
{
|
|
bytes_fd = -1;
|
|
fp = fopen (name, "w");
|
|
if (!fp)
|
|
{
|
|
printf ("GC_enumerator failed to open [%s]\n", name);
|
|
return;
|
|
}
|
|
printf ("GC_enumerator saving summary to [%s]\n", name);
|
|
|
|
// open heap file
|
|
char bytes_name[strlen(name) + 10];
|
|
sprintf (bytes_name, "%s.bytes", name);
|
|
bytes_fd = open (bytes_name, O_CREAT|O_TRUNC|O_WRONLY, 0666);
|
|
if (bytes_fd <= 0)
|
|
{
|
|
printf ("GC_enumerator failed to open [%s]\n", bytes_name);
|
|
return;
|
|
}
|
|
printf ("GC_enumerator saving heap contents to [%s]\n", bytes_name);
|
|
}
|
|
|
|
/*
|
|
sample format of /proc/self/maps
|
|
|
|
0063b000-00686000 rw-p 001fb000 03:01 81993 /avtrex/bin/dumppropapp
|
|
00686000-0072e000 rwxp 00000000 00:00 0
|
|
|
|
These are parsed below as:
|
|
start -end xxxx xxxxxxxx a:b xxxxxxxxxxxxxxx
|
|
|
|
*/
|
|
|
|
|
|
void
|
|
GC_enumerator::print_address_map()
|
|
{
|
|
FILE* fm;
|
|
char buffer[128];
|
|
|
|
fprintf(fp, "---------- Begin address map ----------\n");
|
|
|
|
fm = fopen("/proc/self/maps", "r");
|
|
if (fm == NULL)
|
|
{
|
|
#ifdef HAVE_STRERROR_R
|
|
if (0 == strerror_r (errno, buffer, sizeof buffer))
|
|
fputs (buffer, fp);
|
|
#else
|
|
fputs (strerror (errno), fp);
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
while (fgets (buffer, sizeof buffer, fm) != NULL)
|
|
{
|
|
fputs (buffer, fp);
|
|
char *dash = strchr(buffer, '-');
|
|
char *colon = strchr(buffer, ':');
|
|
if (dash && colon && ((ptrdiff_t)strlen(buffer) > (colon - buffer) + 2))
|
|
{
|
|
char *endp;
|
|
unsigned long start = strtoul(buffer, NULL, 16);
|
|
unsigned long end = strtoul(dash + 1, &endp, 16);
|
|
unsigned long a = strtoul(colon - 2, NULL, 16);
|
|
unsigned long b = strtoul(colon + 1, NULL, 16);
|
|
// If it is an anonymous mapping 00:00 and both readable
|
|
// and writeable then dump the contents of the mapping
|
|
// to the bytes file. Each block has a header of three
|
|
// unsigned longs:
|
|
// 0 - The number sizeof(unsigned long) to detect endianness and
|
|
// structure layout.
|
|
// 1 - The offset in VM.
|
|
// 2 - The Length in bytes.
|
|
// Followed by the bytes.
|
|
if (!a && !b && endp < colon && 'r' == endp[1] && 'w' == endp[2])
|
|
{
|
|
unsigned long t = sizeof(unsigned long);
|
|
write(bytes_fd, (void*)&t, sizeof(t));
|
|
write(bytes_fd, (void*)&start, sizeof(start));
|
|
t = end - start;
|
|
write(bytes_fd, (void*)&t, sizeof(t));
|
|
write(bytes_fd, (void*)start, (end - start));
|
|
}
|
|
}
|
|
}
|
|
fclose(fm);
|
|
}
|
|
fprintf(fp, "---------- End address map ----------\n");
|
|
fflush(fp);
|
|
}
|
|
|
|
void
|
|
GC_enumerator::enumerate()
|
|
{
|
|
print_address_map();
|
|
fprintf(fp, "---------- Begin object map ----------\n");
|
|
if (gc_ok)
|
|
GC_gcollect();
|
|
GC_apply_to_all_blocks(enumerate_callback_adaptor,
|
|
(word)(void*)(this));
|
|
fprintf(fp, "---------- End object map ----------\n");
|
|
fflush(fp);
|
|
|
|
GC_print_debug_info_file(fp);
|
|
fflush(fp);
|
|
GC_print_hblkfreelist_file(fp);
|
|
fflush(fp);
|
|
|
|
close(bytes_fd);
|
|
fclose(fp);
|
|
|
|
GC_clear_stack(0);
|
|
}
|
|
|
|
void
|
|
GC_enumerator::enumerate_callback_adaptor(struct hblk *h,
|
|
word dummy)
|
|
{
|
|
GC_enumerator* pinfo = (GC_enumerator*)dummy;
|
|
pinfo->enumerate_callback(h);
|
|
}
|
|
|
|
void
|
|
GC_enumerator::enumerate_callback(struct hblk *h)
|
|
{
|
|
hdr * hhdr = HDR(h);
|
|
size_t bytes = WORDS_TO_BYTES(hhdr->hb_sz);
|
|
int i;
|
|
|
|
for (i = 0; i == 0 || (i + bytes <= HBLKSIZE); i += bytes)
|
|
{
|
|
int inUse = mark_bit_from_hdr(hhdr,BYTES_TO_WORDS(i)); // in use
|
|
char *ptr = (char*)h+i; // address
|
|
int kind = hhdr->hb_obj_kind; // kind
|
|
void *klass = 0;
|
|
void *data = 0;
|
|
if (kind == GC_gcj_kind
|
|
|| kind == GC_gcj_debug_kind
|
|
|| kind == GC_gcj_debug_kind+1)
|
|
{
|
|
void* v = *(void **)ptr;
|
|
if (v)
|
|
{
|
|
klass = *(void **)v;
|
|
data = *(void **)(ptr + sizeof(void*));
|
|
}
|
|
}
|
|
if (inUse)
|
|
fprintf (fp, "used = %d, ptr = %#lx, size = %zd, kind = %d, "
|
|
"klass = %#lx, data = %#lx\n",
|
|
inUse, (unsigned long)ptr, bytes, kind,
|
|
(unsigned long)klass, (unsigned long)data);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Fill in a char[] with low bytes of the string characters. These
|
|
* methods may be called while an OutOfMemoryError is being thrown, so
|
|
* we cannot call nice java methods to get the encoding of the string.
|
|
*/
|
|
static void
|
|
J2A(::java::lang::String* str, char *dst)
|
|
{
|
|
jchar * pchars = JvGetStringChars(str);
|
|
jint len = str->length();
|
|
int i;
|
|
for (i=0; i<len; i++)
|
|
dst[i] = (char)pchars[i];
|
|
dst[i] = 0;
|
|
}
|
|
|
|
void
|
|
::gnu::gcj::util::GCInfo::dump0 (::java::lang::String * name)
|
|
{
|
|
char n[name->length() + 1];
|
|
J2A(name, n);
|
|
|
|
char temp[name->length() + 20];
|
|
sprintf(temp, "%s%03d", n, GC_dump_count++);
|
|
FILE* fp = fopen(temp, "w");
|
|
|
|
GC_print_debug_info_file(fp);
|
|
|
|
fclose(fp);
|
|
}
|
|
|
|
void
|
|
::gnu::gcj::util::GCInfo::enumerate0 (::java::lang::String * name)
|
|
{
|
|
char n[name->length() + 1];
|
|
J2A(name, n);
|
|
char temp[name->length() + 20];
|
|
sprintf(temp, "%s%03d", n, GC_dump_count++);
|
|
|
|
GC_enumerator x(temp);
|
|
x.enumerate();
|
|
}
|
|
|
|
static char *oomDumpName = NULL;
|
|
|
|
static void *
|
|
nomem_handler(size_t size)
|
|
{
|
|
if (oomDumpName)
|
|
{
|
|
char temp[strlen(oomDumpName) + 20];
|
|
sprintf(temp, "%s%03d", temp, GC_dump_count++);
|
|
printf("nomem_handler(%zd) called\n", size);
|
|
gc_ok--;
|
|
GC_enumerator x(temp);
|
|
x.enumerate();
|
|
gc_ok++;
|
|
}
|
|
return (void*)0;
|
|
}
|
|
|
|
void
|
|
::gnu::gcj::util::GCInfo::setOOMDump0 (::java::lang::String * name)
|
|
{
|
|
char *oldName = oomDumpName;
|
|
oomDumpName = NULL;
|
|
free (oldName);
|
|
|
|
if (NULL == name)
|
|
return;
|
|
|
|
char *n = (char *)malloc(name->length() + 1);
|
|
|
|
J2A(name, n);
|
|
oomDumpName = n;
|
|
GC_oom_fn = nomem_handler;
|
|
}
|
|
|
|
#else // HAVE_PROC_SELF_MAPS
|
|
|
|
void
|
|
::gnu::gcj::util::GCInfo::dump0 (::java::lang::String * name)
|
|
{
|
|
// Do nothing if dumping not supported.
|
|
}
|
|
|
|
void
|
|
::gnu::gcj::util::GCInfo::enumerate0 (::java::lang::String * name)
|
|
{
|
|
// Do nothing if dumping not supported.
|
|
}
|
|
|
|
void
|
|
::gnu::gcj::util::GCInfo::setOOMDump0 (::java::lang::String * name)
|
|
{
|
|
// Do nothing if dumping not supported.
|
|
}
|
|
|
|
#endif // HAVE_PROC_SELF_MAPS
|
|
|