Created
November 24, 2017 20:37
-
-
Save martinhynar/9c75a89e1a43d188498bc03b78060770 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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