Skip to content

Commit

Permalink
JACOBIN-397 Improve tracing (PR #93 from texadactyl)
Browse files Browse the repository at this point in the history
Improve display of objects in traces and flag malformed objects
  • Loading branch information
platypusguy authored Nov 9, 2023
2 parents ddede45 + 45b4d37 commit 9d0434e
Show file tree
Hide file tree
Showing 4 changed files with 91 additions and 45 deletions.
23 changes: 21 additions & 2 deletions src/classloader/javaLangString.go
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,14 @@ func Load_Lang_String() map[string]GMeth {
GFunction: sprintf,
}

// Return the length of a String..
MethodSignatures["java/lang/String.length()I"] =
GMeth{
ParamSlots: 0,
ObjectRef: true,
GFunction: stringLength,
}

return MethodSignatures

}
Expand Down Expand Up @@ -297,8 +305,8 @@ func StringFormatter(params []interface{}) *object.Object {
valuesOut = append(valuesOut, object.GetGoStringFromJavaStringPtr(valuesIn[i]))
//fmt.Printf("DEBUG got a string: %s\n", object.GetGoStringFromJavaStringPtr(valuesIn[i]))
} else {
//str := valuesIn[i].ToString(10)
//fmt.Printf("DEBUG StringFormatter valuesIn[%d] ToString:\n%s", i, str)
//str := valuesIn[i].FormatField(10)
//fmt.Printf("DEBUG StringFormatter valuesIn[%d] FormatField:\n%s", i, str)

// Establish a pointer to the field.
var fldPtr *object.Field
Expand Down Expand Up @@ -351,3 +359,14 @@ func StringFormatter(params []interface{}) *object.Object {
// Return a pointer to an object.Object that wraps the string byte array.
return object.CreateCompactStringFromGoString(&str)
}

func stringLength(params []interface{}) interface{} {
var bytesPtr *[]byte
parmObj := params[0].(*object.Object)
if len(parmObj.FieldTable) > 0 {
bytesPtr = parmObj.FieldTable["value"].Fvalue.(*[]byte)
} else {
bytesPtr = parmObj.Fields[0].Fvalue.(*[]byte)
}
return int64(len(*bytesPtr))
}
10 changes: 4 additions & 6 deletions src/jvm/goFunctionExec.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,8 @@ var localDebugging bool = false
// by run() on the operand stack of the calling function.
func runGframe(fr *frames.Frame) (interface{}, int, error) {
if localDebugging || MainThread.Trace {
traceInfo := fmt.Sprintf("runGframe class: %s, methodName: %s", fr.ClName, fr.MethName)
traceInfo := fmt.Sprintf("runGframe %s.%s, f.OpStack:", fr.ClName, fr.MethName)
_ = log.Log(traceInfo, log.WARNING)
_ = log.Log("runGframe go frame stack:", log.WARNING)
logTraceStack(fr)
}

Expand All @@ -46,7 +45,6 @@ func runGframe(fr *frames.Frame) (interface{}, int, error) {
for _, v := range fr.OpStack {
*params = append(*params, v)
}
// fmt.Printf("runGframe class: %s, methodName: %s, params: %v\n", fr.ClName, fr.MethName, params)

// TODO Validate that a thread pointer is not needed.
// pass a pointer to the thread as the last parameter to the function;
Expand Down Expand Up @@ -93,8 +91,8 @@ func runGmethod(mt classloader.MTentry, fs *list.List, className, methodName, me
// Get the GMeth paramSlots value.
paramSlots := mt.Meth.(classloader.GMeth).ParamSlots
if localDebugging || MainThread.Trace {
traceInfo := fmt.Sprintf("runGmethod %s.%s, paramExtra: %v, methodType: %s, paramSlots: %d, len(f.OpStack): %d, f.TOS: %d",
className, methodName, ObjectRef, methodType, paramSlots, len(f.OpStack), f.TOS)
traceInfo := fmt.Sprintf("runGmethod %s.%s%s, objectRef: %v, paramSlots: %d, f.OpStack:",
className, methodName, methodType, ObjectRef, paramSlots)
_ = log.Log(traceInfo, log.WARNING)
logTraceStack(f)
}
Expand Down Expand Up @@ -148,7 +146,7 @@ func runGmethod(mt classloader.MTentry, fs *list.List, className, methodName, me
// Set the Go frame TOS = parent frame TOS.
gf.TOS = len(gf.OpStack) - 1
if localDebugging || MainThread.Trace {
_ = log.Log("runGmethod go frame stack:", log.WARNING)
_ = log.Log("runGmethod G method OpStack:", log.WARNING)
logTraceStack(gf)
}

Expand Down
46 changes: 22 additions & 24 deletions src/jvm/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -2608,17 +2608,33 @@ func runFrame(fs *list.List) error {
// Log the existing stack
// Could be called for tracing -or- supply info for an error section
func logTraceStack(f *frames.Frame) {
var traceInfo string
var traceInfo, output string
if f.TOS == -1 {
traceInfo = fmt.Sprintf("%67s", "stack empty")
traceInfo = fmt.Sprintf("%55s stack <empty>", "")
_ = log.Log(traceInfo, log.WARNING)
return
}
for ii := 0; ii <= f.TOS; ii++ {
switch f.OpStack[ii].(type) {
case *object.Object:
if object.IsNull(f.OpStack[ii].(*object.Object)) {
output = fmt.Sprintf("<null>")
} else {
objPtr := f.OpStack[ii].(*object.Object)
output = objPtr.FormatField()
}
case *[]uint8:
value := f.OpStack[ii]
strPtr := value.(*[]byte)
str := string(*strPtr)
output = fmt.Sprintf("*[]byte: %-10s", str)
default:
output = fmt.Sprintf("%T %v ", f.OpStack[ii], f.OpStack[ii])
}
if f.TOS == ii {
traceInfo = fmt.Sprintf("%55s TOS [%d] %T %v", "", ii, f.OpStack[ii], f.OpStack[ii])
traceInfo = fmt.Sprintf("%55s TOS [%d] %s", "", ii, output)
} else {
traceInfo = fmt.Sprintf("%55s stack [%d] %T %v", "", ii, f.OpStack[ii], f.OpStack[ii])
traceInfo = fmt.Sprintf("%55s stack [%d] %s", "", ii, output)
}
_ = log.Log(traceInfo, log.WARNING)
}
Expand All @@ -2635,28 +2651,10 @@ func emitTraceData(f *frames.Frame) string {
// if the value at TOS is a string, say so and print the first 10 chars of the string
case *object.Object:
if object.IsNull(f.OpStack[f.TOS].(*object.Object)) {
stackTop = fmt.Sprintf("null")
stackTop = fmt.Sprintf("<null>")
} else {
objPtr := f.OpStack[f.TOS].(*object.Object)
stackTop = objPtr.ToString(50)
/***
obj := *(f.OpStack[f.TOS].(*object.Object))
if obj.Fields != nil && len(obj.Fields) > 0 {
if obj.Fields != nil && obj.Fields[0].Ftype == types.ByteArray { // if it's a string, just show the string
if obj.Fields[0].Fvalue == nil {
stackTop = fmt.Sprintf("[]byte: <nil>")
} else {
strVal := (obj.Fields[0].Fvalue).(*[]byte)
str := string(*strVal)
stackTop = fmt.Sprintf("String: %-10s", str)
}
} else { // so not a byte array (and therefore, not a string)
stackTop = "Object: "
}
} else {
stackTop = "obj.Field[]"
}
***/
stackTop = objPtr.FormatField()
}
case *[]uint8:
value := f.OpStack[f.TOS]
Expand Down
57 changes: 44 additions & 13 deletions src/object/object.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ type MarkWord struct {
// putfield bytecodes are executed.
type Field struct {
Ftype string // what type of value is stored in the field
Fvalue any // the actual value
Fvalue any // the actual value or a pointer to the value (ftype="[something)
}

// Null is the Jacobin implementation of Java's null
Expand All @@ -64,7 +64,7 @@ func IsNull(value any) bool {
}

func toStringHelper(klassString string, field Field) string {
if klassString == filepath.FromSlash("java/lang/String") {
if klassString == filepath.FromSlash(StringClassName) {
return fmt.Sprintf("%s", *field.Fvalue.(*[]byte))
}
switch field.Ftype {
Expand All @@ -78,16 +78,51 @@ func toStringHelper(klassString string, field Field) string {
return fmt.Sprintf("%t", field.Fvalue)
case types.Char:
return fmt.Sprintf("%q", field.Fvalue)
case "Ljava/lang/String;":
return field.Fvalue.(string)
case types.ByteArray:
return fmt.Sprintf("% x", *field.Fvalue.(*[]byte))
bytesPtr := field.Fvalue.(*[]byte)
if bytesPtr == nil {
return "<NIL BYTE ARRAY PTR!>"
}
if len(*bytesPtr) < 1 {
return "<nil>"
}
return fmt.Sprintf("% x", *bytesPtr)
}

return fmt.Sprintf("%v", field.Fvalue)
}

// ToString dumps the contents of an object to a formatted multi-line string
// FormatField creates a string that represents a single field of an Object.
func (objPtr *Object) FormatField() string {
var output string
var klassString string // string class name
obj := *objPtr // whole object
key := "value" // key to the FieldTable map

if obj.Klass != nil {
klassString = *obj.Klass
} else {
klassString = "<class MISSING!>" // Why is there no class name pointer for this object?
}

if len(obj.FieldTable) > 0 {
// Using key="value" in the FieldTable
field := *obj.FieldTable[key]
output = fmt.Sprintf("%s: (%s) %s\n", key, obj.FieldTable[key].Ftype, toStringHelper(klassString, field))
} else {
// Using [0] in the Fields slice
if len(obj.Fields) > 0 {
field := obj.Fields[0]
output += fmt.Sprintf("(%s) %s", obj.Fields[0].Ftype, toStringHelper(klassString, field))
} else {
output = "<field MISSING!>"
}
}

return output
}

// FormatField dumps the contents of an object to a formatted multi-line string
func (objPtr *Object) ToString(indent int) string {
var str string
var klassString string
Expand All @@ -105,21 +140,17 @@ func (objPtr *Object) ToString(indent int) string {
if indent > 0 {
str += strings.Repeat(" ", indent)
}
str += fmt.Sprintf("\tFld: %s: (%s) %s\n", key, obj.FieldTable[key].Ftype, toStringHelper(klassString, *obj.FieldTable[key]))
str += fmt.Sprintf("Fld %s: (%s) %s\n", key, obj.FieldTable[key].Ftype, toStringHelper(klassString, *obj.FieldTable[key]))
}
} else {
//for i, field := range obj.Fields {
// str += fmt.Sprintf("\tFld: %02d: (%s) %s\n", i, field.Ftype, toStringHelper(field))
//}
if indent > 0 {
str += strings.Repeat(" ", indent)
}
if len(obj.Fields) > 0 {
str += fmt.Sprintf("\tFld:(%s) %s", obj.Fields[0].Ftype, toStringHelper(klassString, obj.Fields[0]))
str += fmt.Sprintf("Fld (%s) %s", obj.Fields[0].Ftype, toStringHelper(klassString, obj.Fields[0]))
} else {
str += "\tFld:EMPTY!!!"
str += "Fld <empty>"
}

}

return str
Expand Down

0 comments on commit 9d0434e

Please sign in to comment.