Skip to content

Instantly share code, notes, and snippets.

@martinhynar
Created November 24, 2017 20:37
Show Gist options
  • Save martinhynar/9c75a89e1a43d188498bc03b78060770 to your computer and use it in GitHub Desktop.
Save martinhynar/9c75a89e1a43d188498bc03b78060770 to your computer and use it in GitHub Desktop.
package main
// http://cr.openjdk.java.net/~ohair/openjdk7/jdk7-build-copyright/webrev/jdk/src/share/classes/sun/jvmstat/perfdata/monitor/v2_0/PerfDataBuffer.java-.html
// https://github.com/twitter/commons/blob/master/src/python/twitter/common/java/perfdata/builders/perfdata2.py
import (
"fmt"
"errors"
"io/ioutil"
"encoding/binary"
"bytes"
"bufio"
"time"
"encoding/json"
"strings"
)
var MAGIC = []byte{'\xca', '\xfe', '\xc0', '\xc0'}
const LENGTH int32 = 20
type Event struct {
Timestamp string `json:"timestamp,omitempty"`
Name string `json:"name,omitempty"`
Value int64 `json:"metric,omitempty"`
StringValue string `json:"description,omitempty"`
}
type Prologue struct {
magic []byte
byteOrder binary.ByteOrder
majorVersion byte
minorVersion byte
accessible byte
prologueUsed int32
overflowOffset int32
mTime int64
entryOffset int32
numEntries int32
// artificial
timestamp string
}
type PerfDataEntry struct {
// header
entryLength int32
nameOffset int32
vectorLength int32
dataType string
flags byte
dataUnits byte
dataVar byte
dataOffset int32
// data
name string
value int64
stringValue string
}
// Variability
const (
V_INVALID = iota
V_CONSTANT = iota
V_MONOTONIC = iota
V_VARIABLE = iota
)
// Units
const (
U_INVALID = iota
U_NONE = iota
U_BYTES = iota
U_TICKS = iota
U_EVENTS = iota
U_STRING = iota
U_HERTZ = iota
)
// Data types
const (
T_BOOLEAN = "Z"
T_CHAR = "C"
T_FLOAT = "F"
T_DOUBLE = "D"
T_BYTE = "B"
T_SHORT = "S"
T_INT = "I"
T_LONG string = "J"
T_OBJECT = "L"
T_ARRAY = "["
T_VOID = "V"
)
func check(e error) {
if e != nil {
panic(e)
}
}
func checkMagic(p *Prologue) error {
if(bytes.Compare(MAGIC, p.magic) != 0) {
panic(errors.New("Magic bytes are incorrect."))
}
return nil
}
func ReadPrologue(data []byte, prologue *Prologue) error {
prologue.magic = data[0:4]
if (data[4] == 0) {
prologue.byteOrder = binary.BigEndian
} else {
prologue.byteOrder = binary.LittleEndian
}
prologue.majorVersion = data[5]
prologue.minorVersion = data[6]
prologue.accessible = data[7]
var i32 int32
var i64 int64
var bo = prologue.byteOrder
binary.Read(bytes.NewBuffer(data[8:12]), bo, &i32)
prologue.prologueUsed = i32
binary.Read(bytes.NewBuffer(data[12:16]), bo, &i32)
prologue.overflowOffset = i32
binary.Read(bytes.NewBuffer(data[16:24]), bo, &i64)
prologue.mTime = i64
binary.Read(bytes.NewBuffer(data[24:28]), bo, &i32)
prologue.entryOffset = i32
binary.Read(bytes.NewBuffer(data[28:32]), bo, &i32)
prologue.numEntries = i32
return nil
}
func ReadDataEntry(data []byte, byteOrder binary.ByteOrder, dataEntry *PerfDataEntry) error {
var bo = byteOrder
binary.Read(bytes.NewBuffer(data[0:4]), bo, &dataEntry.entryLength)
binary.Read(bytes.NewBuffer(data[4:8]), bo, &dataEntry.nameOffset)
binary.Read(bytes.NewBuffer(data[8:12]), bo, &dataEntry.vectorLength)
dataEntry.dataType = string(data[12])
dataEntry.flags = data[13]
dataEntry.dataUnits = data[14]
dataEntry.dataVar = data[15]
binary.Read(bytes.NewBuffer(data[16:20]), bo, &dataEntry.dataOffset)
// READ DATA
// name
reader := bufio.NewReader(bytes.NewBuffer(data[dataEntry.nameOffset:dataEntry.nameOffset+50]))
name, _ := reader.ReadSlice('\x00')
dataEntry.name = string(name[:len(name)-1])
//data
if (dataEntry.vectorLength == 0) {
// Long is expected
if (dataEntry.dataType != T_LONG) {
return errors.New(fmt.Sprintf("Unexpected value for entry %s", dataEntry.name))
}
// Read long value
var d int64
s := dataEntry.dataOffset
binary.Read(bytes.NewBuffer(data[s:s+8]), bo, &d)
dataEntry.value = d
} else {
if (dataEntry.dataType == T_BYTE && dataEntry.dataUnits != U_STRING ) {
return errors.New(fmt.Sprintf("Byte monitor with non-string value: %s", dataEntry.name))
}
s := dataEntry.dataOffset
if (dataEntry.dataVar == V_CONSTANT) {
dataEntry.stringValue = string(data[s:s+dataEntry.vectorLength-1])
}
if (dataEntry.dataVar == V_VARIABLE) {
dataEntry.stringValue = strings.Trim(string(data[s:s+dataEntry.vectorLength-1]), "\u0000\u000A\u000D")
}
}
return nil
}
func hasEntry(start int32, length int32, entries int32, expectedEntries int32) bool {
return (start + LENGTH < length) && (entries < expectedEntries)
}
func main() {
dat, err := ioutil.ReadFile("samples/10290")
check(err)
var length = int32(len(dat))
var numEntries int32 = 0
var start int32 = 0
var prologue Prologue
ReadPrologue(dat, &prologue)
checkMagic(&prologue)
var entries = make([]PerfDataEntry, prologue.numEntries)
start = prologue.entryOffset
for (hasEntry(start, length, numEntries, prologue.numEntries)) {
var dataEntry PerfDataEntry
ReadDataEntry(dat[start:], prologue.byteOrder, &dataEntry)
entries[numEntries] = dataEntry
numEntries++
start += dataEntry.entryLength
}
t := time.Now()
timestamp := t.Format(time.RFC3339)
// Print out json's
for _,e := range entries {
var b []byte
if(e.dataType == T_LONG) {
b, err = json.Marshal(struct {
Timestamp string `json:"timestamp,omitempty"`
Name string `json:"name,omitempty"`
Value int64 `json:"metric"`
}{
timestamp, e.name, e.value,
})
fmt.Println(string(b))
}
if(e.dataType == T_BYTE && e.stringValue != "") {
b, err = json.Marshal(struct {
Timestamp string `json:"timestamp,omitempty"`
Name string `json:"name,omitempty"`
Value string `json:"value,omitempty"`
}{
timestamp, e.name, e.stringValue,
})
fmt.Println(string(b))
}
if err != nil {
fmt.Println("error:", err)
}
// os.Stdout.Write(b)
}
}
package main
import (
"fmt"
"testing"
)
var MAGIC = []byte{'\xca', '\xfe', '\xc0', '\xc0'}
const LENGTH int32 = 20
func TestMagicCheck(t *testing.T) {
var p Prologue = Prologue(magic = []byte{'\xca', '\xfe', '\xc0', '\xc0'},)
err := checkMagic(p)
if err != nil {
t.Error("Magic checked incorrectly")
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment