libvassert: vmware VAssert support.
- BSD-licensed Code gratefully taken from the project at http://en.sourceforge.jp/projects/sfnet_vassertlinuxsdk/ - For more information on vmware VAssert, a powerful debugging facility usable under vmware, see: www.vmware.com/pdf/ws65_vassert_programming.pdf
This commit is contained in:
parent
f614d0015a
commit
914d02825f
|
@ -21,7 +21,7 @@ SUBDIR= csu ${LIBCOMPAT_DIR} ${LIBC_DIR} libdriver libnetdriver \
|
|||
libddekit
|
||||
|
||||
.if defined(NBSD_LIBC) && (${NBSD_LIBC} != "no")
|
||||
SUBDIR+= libelf libminc libcrypt libterminfo libcurses
|
||||
SUBDIR+= libelf libminc libcrypt libterminfo libcurses libvassert
|
||||
.endif
|
||||
|
||||
.if ${COMPILER_TYPE} == "ack"
|
||||
|
|
8
lib/libvassert/Makefile
Normal file
8
lib/libvassert/Makefile
Normal file
|
@ -0,0 +1,8 @@
|
|||
# Makefile for libvassert library
|
||||
|
||||
LIB= vassert
|
||||
SRCS= backdoor.S vassert.c
|
||||
INCS+= vassert.h
|
||||
INCSDIR= /usr/include/minix
|
||||
|
||||
.include <bsd.lib.mk>
|
22
lib/libvassert/backdoor.S
Normal file
22
lib/libvassert/backdoor.S
Normal file
|
@ -0,0 +1,22 @@
|
|||
|
||||
.global libvassert_process_backdoor
|
||||
|
||||
libvassert_process_backdoor:
|
||||
push %ebx
|
||||
push %esi
|
||||
mov 0xc(%esp),%ecx
|
||||
mov 0x14(%esp),%edx
|
||||
mov 0x10(%esp),%ebx
|
||||
mov $0x564d5868,%eax
|
||||
out %eax,(%dx)
|
||||
mov 0x18(%esp),%esi
|
||||
mov %eax,(%esi)
|
||||
mov 0x20(%esp),%eax
|
||||
mov %ecx,(%eax)
|
||||
mov 0x24(%esp),%eax
|
||||
mov %edx,(%eax)
|
||||
mov 0x1c(%esp),%eax
|
||||
mov %ebx,(%eax)
|
||||
pop %esi
|
||||
pop %ebx
|
||||
ret
|
298
lib/libvassert/vassert.c
Normal file
298
lib/libvassert/vassert.c
Normal file
|
@ -0,0 +1,298 @@
|
|||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
#include <signal.h>
|
||||
#include <setjmp.h>
|
||||
#include <minix/config.h>
|
||||
#include <minix/const.h>
|
||||
#include <minix/ipc.h>
|
||||
#include <minix/com.h>
|
||||
#include "vassert.h"
|
||||
|
||||
VAssert_StateWrapper vassert_state ALIGNED(VASSERT_PAGE_SIZE);
|
||||
|
||||
#define TRUE 1
|
||||
#define FALSE 0
|
||||
|
||||
#define MAGIC_CMD 0x564d5868
|
||||
#define MAGIC_PORT 0x5658
|
||||
#define HIGH_BAND_PORT 0x5659
|
||||
|
||||
#define BACKDOOR_PORT 51
|
||||
#define BACKDOOR_HB_PORT 1
|
||||
#define CMD_SET_ADDRESS BACKDOOR_PORT|(1<<16)
|
||||
#define CMD_RETURN_REPLAY BACKDOOR_PORT|(2<<16)
|
||||
#define CMD_GO_LIVE BACKDOOR_PORT|(3<<16)
|
||||
#define CMD_LOG BACKDOOR_HB_PORT|(4<<16)
|
||||
#define CMD_SET_RECORD 47
|
||||
|
||||
#define LOG_MAX 512
|
||||
|
||||
typedef char Bool;
|
||||
typedef unsigned int uint32;
|
||||
typedef unsigned long long uint64;
|
||||
|
||||
#ifdef VM_X86_64
|
||||
typedef uint64 VA;
|
||||
#else
|
||||
typedef uint32 VA;
|
||||
#endif
|
||||
|
||||
static sigjmp_buf segv_jmp;
|
||||
|
||||
/*
|
||||
*---------------------------------------------------------------------
|
||||
*
|
||||
* sig_segv --
|
||||
*
|
||||
* Customed SEGV signal handler for VAssert_IsInVM.
|
||||
*
|
||||
* Results:
|
||||
*
|
||||
* None.
|
||||
*
|
||||
* Side effects:
|
||||
* None.
|
||||
*
|
||||
*---------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
static void
|
||||
sig_segv(int sig_no)
|
||||
{
|
||||
/* jumping to error handling in VAssert_IsInVM. */
|
||||
siglongjmp(segv_jmp, 1);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
*---------------------------------------------------------------------
|
||||
*
|
||||
* VAssert_IsInVM --
|
||||
*
|
||||
* Check if we are in virtual world.
|
||||
*
|
||||
* Results:
|
||||
*
|
||||
* Return TRUE on success, or FALSE on failure.
|
||||
*
|
||||
* Side effects:
|
||||
* None.
|
||||
*
|
||||
*---------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
static Bool
|
||||
VAssert_IsInVM(void)
|
||||
{
|
||||
uint32 eax, ebx, ecx, edx;
|
||||
static Bool inVM = FALSE;
|
||||
static Bool tested = FALSE;
|
||||
if (!tested) {
|
||||
/* Error handling. */
|
||||
if (sigsetjmp(segv_jmp, 0) != 0) {
|
||||
signal(SIGSEGV, SIG_DFL);
|
||||
inVM = FALSE;
|
||||
return inVM;
|
||||
}
|
||||
tested = TRUE;
|
||||
/* Install custom handler. */
|
||||
signal(SIGSEGV, sig_segv);
|
||||
/* Test if we are in a VM. */
|
||||
libvassert_process_backdoor(0x0a, 0, MAGIC_PORT, &eax, &ebx, &ecx, &edx);
|
||||
signal(SIGSEGV, SIG_DFL);
|
||||
inVM = TRUE;
|
||||
}
|
||||
return inVM;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
*---------------------------------------------------------------------
|
||||
*
|
||||
* VAssert_Init --
|
||||
*
|
||||
* Tell vmx that vassert is inited.
|
||||
*
|
||||
* Results:
|
||||
*
|
||||
* Return 0 on success, or -1 on failure.
|
||||
*
|
||||
* Side effects:
|
||||
* None
|
||||
*
|
||||
*---------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
char
|
||||
VAssert_Init(void)
|
||||
{
|
||||
uint32 eax, ebx, ecx, edx;
|
||||
VA page_address = (VA) &vassert_state.inReplay, ph;
|
||||
if (!VAssert_IsInVM()) {
|
||||
return -1;
|
||||
}
|
||||
bzero((char*) &vassert_state, sizeof vassert_state);
|
||||
#ifndef __minix
|
||||
/* Lock the page. */
|
||||
if (mlock(&vassert_state, sizeof vassert_state)) {
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* vmware expects a linear address (or is simply forgetting
|
||||
* to adjust the given address for segments)
|
||||
*/
|
||||
|
||||
if(sys_umap(SELF, D, page_address, 1, &ph)) {
|
||||
printf("VAssert_Init: sys_umap failed\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
libvassert_process_backdoor(CMD_SET_ADDRESS, ph,
|
||||
MAGIC_PORT|(1<<16), &eax, &ebx, &ecx, &edx);
|
||||
|
||||
return (eax != -1) ? 0 : -1;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
*---------------------------------------------------------------------
|
||||
*
|
||||
* VAssert_Uninit --
|
||||
*
|
||||
* Tell vmx that vassert is finalized.
|
||||
*
|
||||
* Results:
|
||||
*
|
||||
* Return 0 on success, or -1 on failure.
|
||||
*
|
||||
* Side effects:
|
||||
* None
|
||||
*
|
||||
*---------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
char
|
||||
VAssert_Uninit(void)
|
||||
{
|
||||
unsigned int eax, ebx, ecx, edx;
|
||||
if (!VAssert_IsInVM()) {
|
||||
return -1;
|
||||
}
|
||||
libvassert_process_backdoor(CMD_SET_ADDRESS, 0, MAGIC_PORT|(0<<16), &eax, &ebx, &ecx, &edx);
|
||||
return (eax != -1) ? 0 : 1;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
*---------------------------------------------------------------------
|
||||
*
|
||||
* VAssert_LogMain --
|
||||
*
|
||||
* Print message to a text file on host side.
|
||||
*
|
||||
* Results:
|
||||
* None
|
||||
*
|
||||
* Side effects:
|
||||
* Write to a text file with fixed name.
|
||||
* If the file exists, host UI will ask for append/replace/ignore
|
||||
*
|
||||
*---------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
void
|
||||
VAssert_LogMain(const char *format, ...)
|
||||
{
|
||||
unsigned int eax, ebx, ecx, edx;
|
||||
char buf[LOG_MAX];
|
||||
unsigned int len = 0;
|
||||
va_list ap;
|
||||
va_start(ap, format);
|
||||
len = vsnprintf(buf, LOG_MAX, format, ap);
|
||||
va_end(ap);
|
||||
__asm__ __volatile__("cld; rep outsb;"
|
||||
: "=a"(eax), "=b"(ebx), "=c"(ecx), "=d"(edx)
|
||||
: "0"(MAGIC_CMD), "1"(CMD_LOG), "2"(len), "d"(HIGH_BAND_PORT), "S"(buf)
|
||||
: "memory"
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
*---------------------------------------------------------------------
|
||||
*
|
||||
* VAssert_GoLiveMain --
|
||||
*
|
||||
* Make the vm which is in replay exit replay.
|
||||
*
|
||||
* Results:
|
||||
* None
|
||||
*
|
||||
* Side effects:
|
||||
* Replay is stopped.
|
||||
*
|
||||
*---------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
void
|
||||
VAssert_GoLiveMain()
|
||||
{
|
||||
unsigned int eax, ebx, ecx, edx;
|
||||
vassert_state.inReplay = 0;
|
||||
libvassert_process_backdoor(CMD_GO_LIVE, 0, MAGIC_PORT, &eax, &ebx, &ecx, &edx);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
*---------------------------------------------------------------------
|
||||
*
|
||||
* VAssert_ReturnToReplayMain --
|
||||
*
|
||||
* Called after the custom work is done, and replay is to continue.
|
||||
*
|
||||
* Results:
|
||||
* None
|
||||
*
|
||||
* Side effects:
|
||||
* Replay is continued from pause.
|
||||
*
|
||||
*---------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
void
|
||||
VAssert_ReturnToReplayMain()
|
||||
{
|
||||
unsigned int eax, ebx, ecx, edx;
|
||||
libvassert_process_backdoor(CMD_RETURN_REPLAY, 0, MAGIC_PORT, &eax, &ebx, &ecx, &edx);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
*---------------------------------------------------------------------
|
||||
*
|
||||
* VAssert_SetRecordingMain --
|
||||
*
|
||||
* Ask vmx for starting or stopping recording.
|
||||
*
|
||||
* Results:
|
||||
*
|
||||
* Return TRUE on success, or FALSE on failure.
|
||||
*
|
||||
* Side effects:
|
||||
* Recording is started or stopped.
|
||||
*
|
||||
*---------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
char
|
||||
VAssert_SetRecordingMain(char start)
|
||||
{
|
||||
uint32 eax, ebx, ecx, edx;
|
||||
if (!VAssert_IsInVM()) {
|
||||
return FALSE;
|
||||
}
|
||||
libvassert_process_backdoor(CMD_SET_RECORD, start ? 1 : 2, MAGIC_PORT, &eax, &ebx, &ecx, &edx);
|
||||
return (eax == 1) ? TRUE : FALSE;
|
||||
}
|
||||
|
138
lib/libvassert/vassert.h
Normal file
138
lib/libvassert/vassert.h
Normal file
|
@ -0,0 +1,138 @@
|
|||
#ifndef _VASSERT_H_
|
||||
#define _VASSERT_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif /*__cplusplus*/
|
||||
|
||||
#define ALIGNED(n) __attribute__((__aligned__(n)))
|
||||
#define VASSERT_TRIGGER_OFFSET 1221
|
||||
#define VASSERT_PAGE_SIZE 4096
|
||||
|
||||
/* Need to align at 4K. */
|
||||
/* Ensure the inReplay flag is on its own page. */
|
||||
#pragma pack(1)
|
||||
typedef struct VAssert_StateWrapper {
|
||||
char space1[VASSERT_TRIGGER_OFFSET];
|
||||
volatile char inReplay;
|
||||
char space[VASSERT_PAGE_SIZE - VASSERT_TRIGGER_OFFSET - sizeof(char)];
|
||||
} VAssert_StateWrapper;
|
||||
#pragma pack()
|
||||
|
||||
extern VAssert_StateWrapper vassert_state;
|
||||
|
||||
/*
|
||||
* User-selectable standard functions.
|
||||
* XXX: Document these, in coordination with the SDK docs.
|
||||
*/
|
||||
|
||||
#if defined(__KERNEL__)
|
||||
# define KERNEL_VASSERT
|
||||
#endif
|
||||
|
||||
#ifdef KERNEL_VASSERT
|
||||
|
||||
# ifndef VASSERT_CUSTOM_ASSERT
|
||||
# define VASSERT_CUSTOM_ASSERT(expr)
|
||||
# endif
|
||||
|
||||
# ifndef VASSERT_CUSTOM_ABORT
|
||||
# include <linux/kernel.h>
|
||||
# define VASSERT_CUSTOM_ABORT() ((void)0) // printk(KERN_ALERT"VAssert abort at %s: %d", __FILE__, __LINE__)
|
||||
# endif
|
||||
|
||||
# ifndef VASSERT_CUSTOM_LOG
|
||||
# include <linux/kernel.h>
|
||||
# define VASSERT_CUSTOM_LOG printk
|
||||
# endif
|
||||
|
||||
#else
|
||||
# ifndef VASSERT_CUSTOM_ASSERT
|
||||
# include <assert.h>
|
||||
# define VASSERT_CUSTOM_ASSERT assert
|
||||
# endif
|
||||
|
||||
# ifndef VASSERT_CUSTOM_ABORT
|
||||
# include <stdlib.h>
|
||||
# define VASSERT_CUSTOM_ABORT abort
|
||||
# endif
|
||||
|
||||
# ifndef VASSERT_CUSTOM_LOG
|
||||
# include <stdio.h>
|
||||
# define VASSERT_CUSTOM_LOG printf
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/* Results: 0 if successful, -1 if not. */
|
||||
// XXX need to automatic de-register trigger page when the program quits
|
||||
extern char VAssert_Init(void);
|
||||
extern char VAssert_Uninit(void);
|
||||
|
||||
/*
|
||||
* These functions should not be called directly; they need to be wrapped.
|
||||
*/
|
||||
extern void VAssert_LogMain(const char *format, ...);
|
||||
extern void VAssert_GoLiveMain(void);
|
||||
extern void VAssert_ReturnToReplayMain(void);
|
||||
extern char VAssert_Trace(size_t max_size);
|
||||
|
||||
#ifdef VASSERT_ALWAYS_EXECUTE
|
||||
|
||||
#define VAssert_Assert(expr) \
|
||||
do { \
|
||||
VASSERT_CUSTOM_ASSERT(expr); \
|
||||
} while (0)
|
||||
|
||||
#define VAssert_Log(args) \
|
||||
do { \
|
||||
VASSERT_CUSTOM_LOG args; \
|
||||
} while (0)
|
||||
|
||||
#define VAssert_BeginBlock
|
||||
#define VAssert_EndBlock
|
||||
|
||||
#else /* VASSERT_ALWAYS_EXECUTE */
|
||||
|
||||
#define VAssert_Assert(expr) \
|
||||
do { \
|
||||
if (vassert_state.inReplay) { \
|
||||
if (!(expr)) { \
|
||||
VAssert_GoLiveMain(); \
|
||||
VASSERT_CUSTOM_ABORT(); \
|
||||
} else { \
|
||||
VAssert_ReturnToReplayMain(); \
|
||||
} \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define VAssert_Log(args) \
|
||||
do { \
|
||||
if (vassert_state.inReplay) { \
|
||||
VAssert_LogMain args; \
|
||||
VAssert_ReturnToReplayMain(); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define VAssert_BeginBlock if (vassert_state.inReplay)
|
||||
#define VAssert_EndBlock VAssert_ReturnToReplayMain()
|
||||
|
||||
#endif /* VASSERT_ALWAYS_EXECUTE */
|
||||
|
||||
/*
|
||||
* Record/Replay functionality
|
||||
*/
|
||||
/*
|
||||
* Results: True if successful, false if not.
|
||||
* Input: True to start recording, false to stop.
|
||||
*/
|
||||
extern char VAssert_SetRecordingMain(char start);
|
||||
|
||||
#define VAssert_StartRecording() VAssert_SetRecordingMain(1)
|
||||
#define VAssert_StopRecording() VAssert_SetRecordingMain(0)
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /*__cplusplus*/
|
||||
|
||||
#endif /*_VASSERT_H_*/
|
Loading…
Reference in a new issue