/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
// not available on macosx #include <gelf.h>
#include "libjvm_db.h"
#include "JvmOffsets.h"
#ifdef COMPILER2
#define X86_COMPILER2
#endif /* COMPILER2 */
#endif /* i386 */
typedef struct {
} Jframe_t;
static int debug = 0;
if (debug) {
}
}
if (debug) {
}
}
if (debug) {
}
}
typedef struct VMStructEntry {
/* ("offset" can not be reused because of apparent SparcWorks compiler bug */
/* in generation of initializer data) */
/* Prototyping inlined methods */
typedef struct vframe {
} Vframe_t;
typedef struct frame {
} Frame_t;
typedef struct Nmethod_t {
struct jvm_agent* J;
int vf_cnt;
} Nmethod_t;
struct jvm_agent {
struct ps_prochandle* P;
/* Volatiles */
Nmethod_t *N; /*Inlined methods support */
};
static int
char *buf, /* caller's buffer */
{
if (*buf == '\0') {
return PS_OK;
}
addr += 1;
buf += 1;
}
return -1;
}
return err;
}
switch (DATA_MODEL) {
case PR_MODEL_LP64:
break;
case PR_MODEL_ILP32:
break;
}
return err;
}
int err;
if (ptr != 0) {
}
return PS_OK;
fail:
return err;
}
int err;
return PS_OK;
fail:
return err;
}
int err;
break;
}
}
}
}
}
}
}
return PS_OK;
fail:
return -1;
}
int err;
return PS_OK;
fail:
return err;
}
int err;
} else {
J->Use_Compressed_Oops = 0;
}
err = ps_pread(J->P, J->Universe_narrow_oop_shift_address, &J->Universe_narrow_oop_shift, sizeof(uint32_t));
&J->SIZE_CodeCache_log2_segment, sizeof(J->SIZE_CodeCache_log2_segment));
return PS_OK;
fail:
return err;
}
/* make sure the code cache is up to date */
}
return (p - J->CodeCache_low) >> J->SIZE_CodeCache_log2_segment;
}
return J->CodeCache_low + (i << J->SIZE_CodeCache_log2_segment);
}
int err;
*startp = 0;
if (tag == 0xff)
return PS_OK;
while (tag > 0) {
}
if (used) {
}
}
return PS_OK;
fail:
return -1;
}
return err;
}
*valuep = -1;
return -1;
}
jvm_agent_t* J;
int err;
if (vers != JVM_DB_VERSION) {
return NULL;
}
if (debug) {
#ifdef X86_COMPILER2
#endif /* X86_COMPILER2 */
}
J->P = P;
// Initialize the initial previous frame
err = parse_vmstructs(J);
err = read_volatiles(J);
return J;
fail:
Jagent_destroy(J);
return NULL;
}
if (J != NULL) {
free(J);
}
}
int err;
// If UseCompressedOops, this was a compressed oop.
if (J->Use_Compressed_Oops != 0) {
&cklass);
// decode heap oop, same as oop.inline.hpp
} else {
}
return klass == J->Universe_methodKlassObj;
fail:
return 0;
}
static int
{
short nameIndex;
short signatureIndex;
short klassSymbolLength;
short nameSymbolLength;
short signatureSymbolLength;
int err;
/* To get name string */
err = read_pointer(J, constantPool + nameIndex * POINTER_SIZE + SIZE_constantPoolOopDesc, &nameSymbol);
// The symbol is a CPSlot and has lower bit set to indicate metadata
/* To get signature string */
err = read_pointer(J, constantPool + signatureIndex * POINTER_SIZE + SIZE_constantPoolOopDesc, &signatureSymbol);
/* To get klass string */
result[0] = '\0';
size -= 1;
return PS_OK;
fail:
if (debug) {
}
return -1;
}
{
jvm_agent_t *J = N->J;
if (debug > 2 )
/* Instructions */
/* Oops */
/* scopes_pcs */
/* scopes_data */
if (debug > 2 ) {
N->scopes_data_end = N->scopes_pcs_beg;
N->instrs_beg, N->instrs_end);
N->deopt_beg);
N->orig_pc_offset);
N->scopes_data_beg, N->scopes_data_end);
N->scopes_pcs_beg, N->scopes_pcs_end);
}
return PS_OK;
fail:
return err;
}
static int
{
int shift = 0;
int value = 0;
// Constants for UNSIGNED5 coding of Pack200
// see compressedStream.hpp
enum {
H = 1<<lg_H,
L = (1<<BitsPerByte)-H,
};
int i;
if (debug > 2)
if ( sum >= L ) {
// Read maximum of 5 total bytes (we've already read 1).
// See CompressedReadStream::read_int_mb
for ( i = 0; i < 4; i++) {
if (ch < L ) {
return PS_OK;
}
}
}
return PS_OK;
fail:
return err;
}
static int
{
if (debug > 2)
if (next == 0) {
if (debug > 2)
return 1; /* stream terminated */
}
if (next == 0xFF) {
if (debug > 2)
/* Escape character, regular compression used */
*line += line_delta;
if (debug > 2) {
}
} else {
/* Single byte compression used */
if (debug > 2) {
}
}
if (debug > 2)
return PS_OK;
fail:
if (debug)
return err;
}
static int
{
if (debug > 2) {
}
if (!(access_flags & constMethodOopDesc_has_linenumber_table)) {
if (debug > 2)
return PS_OK;
}
/* The line numbers are a short array of 2-tuples [start_pc, line_number].
* Not necessarily sorted and not necessarily one-to-one.
*/
/* inlined_table_start() */
if (debug > 2) {
}
/* perfect match */
if (debug > 2)
return PS_OK;
} else {
if (debug > 2) {
}
}
}
}
if (debug > 2)
return PS_OK;
fail:
if (debug)
return err;
}
static int
{
if (debug > 2) {
}
return PS_OK;
fail:
return err;
}
/* Finds a PcDesc with real-pc equal to N->pc */
{
if (debug > 2)
N->vf_cnt = 0;
N->pc_desc = 0;
/* In general, this fragment should work */
if (pc_diff == 0) {
if (debug) {
}
return PS_OK;
}
/* This fragment is to be able to find out an appropriate
* pc_desc entry even if pc_desc info is inaccurate.
*/
}
}
if (debug) {
if (pc_diff < 20)
else
}
return PS_OK;
fail:
return err;
}
static int
{
if (debug > 2) {
}
if (debug > 2) {
}
return PS_OK;
fail:
return err;
}
if (debug > 2) {
}
&decode_offset, SZ32);
while (decode_offset > 0) {
if (debug > 2) {
}
return -1;
}
N->vf_cnt++;
if (debug > 2) {
}
}
}
if (debug > 2) {
}
return PS_OK;
fail:
if (debug) {
}
return err;
}
static int
char *result,
) {
Nmethod_t *N;
int deoptimized = 0;
if (debug) {
}
if (J->N == NULL) {
}
N = J->N;
N->J = J;
err = nmethod_info(N);
if (debug) {
}
/* check for a deoptimized frame */
if (debug) {
}
} else {
}
if (debug) {
}
deoptimized = 1;
}
err = pc_desc_at(N);
if (N->pc_desc > 0) {
err = scopeDesc_chain(N);
}
result[0] = COMP_METHOD_SIGN;
if (N->vf_cnt > 0) {
} else {
}
if (deoptimized) {
} else {
}
if (debug)
return PS_OK;
fail:
if (debug)
return err;
}
switch (DATA_MODEL) {
case PR_MODEL_LP64:
case PR_MODEL_ILP32:
default:
}
}
static int
char *result,
) {
if (debug)
if (debug)
if (bci > 0) {
}
if (debug) {
}
return PS_OK;
fail:
if (debug)
return err;
}
static int
{
*is_interpreted = 0;
result[0] = '\0';
if (vtbl == J->nmethod_vtbl) {
if (debug) {
}
} else if (vtbl == J->BufferBlob_vtbl) {
const char * name;
/*
* Temporary usage of string "Interpreter".
* We need some other way to distinguish "StubRoutines"
* and regular interpreted frames.
*/
*is_interpreted = 1;
if (is_methodOop(J, J->methodOopPtr)) {
}
}
} else {
}
/* return PS_OK; */
} else {
const char * name;
} else {
}
}
#ifdef X86_COMPILER2
if (vtbl != J->RuntimeStub_vtbl) {
int frame_size;
&frame_size, SZ32);
// frame_size is in words, we want bytes.
/*
in the initial entry to a set of stack frames containing server frames
will pretty much be nonsense. We can detect that nonsense by looking to
see if the PC we received is correct if we look at the expected storage
location in relation to the FP (ie. POINTER_SIZE(FP) )
*/
// Either we couldn't even read at the "fp" or the pc didn't match
// both are sure clues that the fp is bogus. We no search the stack
// for a reasonable number of words trying to find the bogus fp
// and the current pc in adjacent words. The we will be able to
// deduce an approximation of the frame pointer and actually get
// the correct stack pointer. Which we can then unwind for the
// next frame.
int i;
base += POINTER_SIZE;
if (debug) {
}
break;
}
}
}
if ( prev_fp != 0 ) {
// real_sp is the sp we should have received for this frame
// +POINTER_SIZE because callee owns the return address so caller's sp is +1 word
return PS_OK;
}
}
/* A prototype to workaround FP absence */
/*
* frame_size can be 0 for StubRoutines (1) frame.
* In this case it should work with fp as usual.
*/
if (frame_size > 0) {
} else {
}
if (debug) {
result, frame_size);
}
}
#endif /* X86_COMPILER2 */
return PS_OK;
fail:
return err;
}
{
Nmethod_t *N = J->N;
return -1;
}
name[0] = COMP_METHOD_SIGN;
if (debug) {
}
return PS_OK;
fail:
if (debug) {
}
return err;
}
/* arguments given to read_pointer need to be worst case sized */
int is_interpreted = 0;
if (J == NULL) {
return -1;
}
read_volatiles(J);
if (debug)
/* The following workaround is for SPARC. CALL instruction occupates 8 bytes.
* In the pcDesc structure return pc offset is recorded for CALL instructions.
* regs[R_PC] contains a CALL instruction pc offset.
*/
pc += 8;
if (debug > 2) {
fprintf(stderr, "\nregs[R_I1]=%lx, regs[R_I2]=%lx, regs[R_I5]=%lx, regs[R_L1]=%lx, regs[R_L2]=%lx\n",
}
#ifdef X86_COMPILER2
/* A workaround for top java frames */
#else
#endif /* COMPILER2 */
}
if (debug > 2) {
}
methodOopPtr = 0;
}
sender_sp = 0;
}
bcx = 0;
}
#endif /* i386 */
J->methodOopPtr = methodOopPtr;
/* On x86 with C2 JVM: native frame may have wrong regs[R_FP]
* For example: JVM_SuspendThread frame poins to the top interpreted frame.
* If we call is_methodOop(J, methodOopPtr) before codecache_contains(J, pc)
* then we go over and omit both: nmethod and I2CAdapter frames.
* Note, that regs[R_PC] is always correct if frame defined correctly.
* So it is better to call codecache_contains(J, pc) from the beginning.
*/
#ifndef X86_COMPILER2
if (is_methodOop(J, J->methodOopPtr)) {
/* If the methodOopPtr is a method then this is highly likely to be
an interpreter frame */
if (result >= 0) {
is_interpreted = 1;
}
} else
#endif /* ! X86_COMPILER2 */
if (codecache_contains(J, pc)) {
}
#ifdef X86_COMPILER2
else if (is_methodOop(J, J->methodOopPtr)) {
/* If the methodOopPtr is a method then this is highly likely to be
an interpreter frame */
if (result >= 0) {
is_interpreted = 1;
}
}
#endif /* X86_COMPILER2 */
else {
if (debug) {
}
result = -1;
}
if (!is_interpreted) {
sender_sp = 0;
}
#ifdef X86_COMPILER2
}
// This seems dubious
if (debug > 2) {
printf("Jlookup_by_regs: (update pc) jframe->new_fp: %#llx, jframe->new_pc: %#llx\n",
}
}
#endif /* X86_COMPILER2 */
if (debug)
return result;
fail:
return err;
}
#ifdef X86_COMPILER2
if (debug > 0) {
fprintf(stderr, "update_gregs: before update sp = 0x%llx, fp = 0x%llx, pc = 0x%llx\n", gregs[R_SP], gregs[R_FP], gregs[R_PC]);
}
/*
* A workaround for java C2 frames with unconventional FP.
*/
} else {
// *((uintptr_t *) &gregs[R_SP]) = (uintptr_t) gregs[R_FP] + 2 * POINTER_SIZE;
}
}
}
if (debug > 0) {
fprintf(stderr, "update_gregs: after update sp = 0x%llx, fp = 0x%llx, pc = 0x%llx\n", gregs[R_SP], gregs[R_FP], gregs[R_PC]);
}
#endif /* X86_COMPILER2 */
}
/*
* Iterates over java frames at current location given by 'gregs'.
*
* Returns -1 if no java frames are present or if an error is encountered.
* Returns the result of calling 'func' if the return value is non-zero.
* Returns 0 otherwise.
*/
int i = 0, res;
#ifdef X86_COMPILER2
if (debug > 0) {
fprintf(stderr, "Jframe_iter: Entry sp = 0x%llx, fp = 0x%llx, pc = 0x%llx\n", gregs[R_SP], gregs[R_FP], gregs[R_PC]);
}
#endif /* X86_COMPILER2 */
return (-1);
if (res != 0) {
return (res);
}
if (res != 0) {
return (res);
}
}
return (0);
}