Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enhancement #22

Merged
merged 24 commits into from
Aug 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
1ead227
parser_test: ReportAllocs
zdyj3170101136 Jul 28, 2023
39df228
reader: avoid memory escape and unsafe byte to string
zdyj3170101136 Jul 27, 2023
e587646
types: do not malloc base type
zdyj3170101136 Jul 28, 2023
2aa0b33
types: batch allocate StackFrame
zdyj3170101136 Jul 31, 2023
1dcd969
batch allocate constants
zdyj3170101136 Jul 31, 2023
5660ef6
cache typeFn in ClassMetadata
zdyj3170101136 Jul 31, 2023
a0d5347
checkpoint: preallocate class
zdyj3170101136 Jul 31, 2023
a1b27a8
preallocate StackTrace field Frames
zdyj3170101136 Jul 31, 2023
550121d
preallocate StackFrame
zdyj3170101136 Jul 31, 2023
a526cc1
stream chunk reader and cache event
zdyj3170101136 Aug 1, 2023
7151b79
classes store pointer to ClassMetadata
zdyj3170101136 Aug 1, 2023
91d9ca7
types: enhancement range class.Fields
zdyj3170101136 Aug 1, 2023
d66951d
types: remove useless resolve
zdyj3170101136 Aug 1, 2023
c6b4fc2
checkpoint: fix preallocate StackTrace field Frames
zdyj3170101136 Aug 2, 2023
d508791
checkpoint: fix preallocate StackTrace field Frames
zdyj3170101136 Aug 3, 2023
3e43566
let unsafeByteToString configurable
zdyj3170101136 Aug 7, 2023
1ae4392
move parseBaseTypeAndDrops global and add test
zdyj3170101136 Aug 7, 2023
fcbb533
fix conflict
zdyj3170101136 Aug 7, 2023
dca9708
add async-profiler test
zdyj3170101136 Aug 7, 2023
de55962
fix example_parsed.json.gz
zdyj3170101136 Aug 7, 2023
a06f393
fix getPointerToStackFrames
zdyj3170101136 Aug 7, 2023
16c565f
move unsafeByteToString into ChunkParseOptions
zdyj3170101136 Aug 7, 2023
5128897
Merge remote-tracking branch 'offical/main' into w
zdyj3170101136 Aug 7, 2023
9f3560f
modify README.md
zdyj3170101136 Aug 7, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,21 @@ The parser API is pretty straightforward:
func Parse(r io.Reader) ([]Chunk, error)
```

Parser returns a slice of chunks, each containing a slice of events. It should be used like this:
Parser returns a slice of chunks, for each chunk call chunk.Next and then read chunk.Event.

It should be used like this:

```go
chunks, err := parser.Parse(reader)
for _, chunk := range chunks {
for chunk.Next() {
chunk.Event // it may be reused, copy it if you need the event after another call to Next
}
err = chunk.Err()
if err != nil {
panic(err)
}
}
```

Check the [main](./main.go) package for further details. It can also be used to validate the parser works with your data and get some basic stats.
Expand Down
127 changes: 120 additions & 7 deletions parser/checkpoint.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,27 +39,140 @@ func (c *CheckpointEvent) Parse(r reader.Reader, classes ClassMap, cpools PoolMa
if err != nil {
return fmt.Errorf("unable to parse constant pool class: %w", err)
}
m, err := r.VarInt()
if err != nil {
return fmt.Errorf("unable to parse constant pool's number of constants: %w", err)
}
cm, ok := cpools[int(classID)]
if !ok {
cpools[int(classID)] = &CPool{Pool: make(map[int]ParseResolvable)}
cpools[int(classID)] = &CPool{Pool: make(map[int]ParseResolvable, m)}
cm = cpools[int(classID)]
}
m, err := r.VarInt()
if err != nil {
return fmt.Errorf("unable to parse constant pool's number of constants: %w", err)
class, ok := classes[int(classID)]
if !ok {
return fmt.Errorf("unexpected class %d", classID)
}
var (
results []ParseResolvable
preAllocateResults = true
contantsSlice = getConstantsSlice(int(m), class.numConstants)
)
// preallocate common class in async-profiler
results = make([]ParseResolvable, m)
switch class.Name {
case "java.lang.Thread":
threads := make([]Thread, m)
for i := range threads {
threads[i].constants = contantsSlice[i]
results[i] = &threads[i]
}
case "jdk.types.StackTrace":
var classStackFrames *ClassMetadata
for _, class := range classes {
if class.Name == "jdk.types.StackFrame" {
classStackFrames = class
break
}
}
var (
stackFrames []StackFrame
indexStackFrames int
)
createStackFrames := func() ParseResolvable {
if indexStackFrames >= len(stackFrames) {
stackFrames = make([]StackFrame, m)
contantsSlice = getConstantsSlice(int(m), classStackFrames.numConstants)
for i := range stackFrames {
stackFrames[i].constants = contantsSlice[i]
}
indexStackFrames = 0
}
result := &stackFrames[indexStackFrames]
indexStackFrames++
return result
}
for i, class := range classes {
if class.Name == "jdk.types.StackFrame" {
class.typeFn = createStackFrames
classes[i] = class
break
}
}
var pointerToStackFrames []*StackFrame
indexPointerToStackFrames := 0
getPointerToStackFrames := func(n int) []*StackFrame {
if n > int(m) {
return make([]*StackFrame, n)[:0]
}
if indexPointerToStackFrames+n > len(pointerToStackFrames) {
pointerToStackFrames = make([]*StackFrame, m)
indexPointerToStackFrames = 0
}
result := pointerToStackFrames[indexPointerToStackFrames : indexPointerToStackFrames+n]
indexPointerToStackFrames += n
return result[:0]
}
stackTraces := make([]StackTrace, m)
for i := range stackTraces {
stackTraces[i].getPointerToStackFrames = getPointerToStackFrames
stackTraces[i].constants = contantsSlice[i]
results[i] = &stackTraces[i]
}
case "jdk.types.Method":
methods := make([]Method, m)
for i := range methods {
methods[i].constants = contantsSlice[i]
results[i] = &methods[i]
}
case "java.lang.Class":
classes := make([]Class, m)
for i := range classes {
classes[i].constants = contantsSlice[i]
results[i] = &classes[i]
}
case "jdk.types.Package":
packages := make([]Package, m)
for i := range packages {
packages[i].constants = contantsSlice[i]
results[i] = &packages[i]
}
case "jdk.types.Symbol":
symbols := make([]Symbol, m)
for i := range symbols {
symbols[i].constants = contantsSlice[i]
results[i] = &symbols[i]
}
default:
preAllocateResults = false
}
// TODO: assert m is small enough
for j := 0; j < int(m); j++ {
idx, err := r.VarLong()
if err != nil {
return fmt.Errorf("unable to parse contant's index: %w", err)
}
v, err := ParseClass(r, classes, cpools, classID)
if err != nil {
return fmt.Errorf("unable to parse constant type %d: %w", classID, err)

var v ParseResolvable
if preAllocateResults {
v = results[j]
err = v.Parse(r, classes, cpools, class)
} else {
v, err = ParseClass(r, classes, cpools, classID)
if err != nil {
return fmt.Errorf("unable to parse constant type %d: %w", classID, err)
}
}
cm.Pool[int(idx)] = v
}
}
return nil
}

func getConstantsSlice(size int, num int) [][]constant {
constants := make([]constant, size*num)
contantsSlice := make([][]constant, size)
for i := range contantsSlice {
contantsSlice[i] = constants[i*num : (i+1)*num][:0]
}
return contantsSlice
}
Loading