mirror of
https://github.com/jimeh/rbheap.git
synced 2026-02-19 12:56:46 +00:00
Migrate leak related structs to leak package
This commit is contained in:
120
leak/dump.go
Normal file
120
leak/dump.go
Normal file
@@ -0,0 +1,120 @@
|
||||
package leak
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"sort"
|
||||
)
|
||||
|
||||
// NewDump returns a *Dump instance populated with the specified file path.
|
||||
func NewDump(filePath string) *Dump {
|
||||
return &Dump{FilePath: filePath}
|
||||
}
|
||||
|
||||
// Dump contains all relevant data for a single heap dump.
|
||||
type Dump struct {
|
||||
FilePath string
|
||||
Index []*string
|
||||
Entries map[string]*Entry
|
||||
}
|
||||
|
||||
// Process processes the heap dump
|
||||
func (s *Dump) Process() error {
|
||||
file, err := os.Open(s.FilePath)
|
||||
defer file.Close()
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
s.Entries = map[string]*Entry{}
|
||||
|
||||
var offset int64 = -1
|
||||
reader := bufio.NewReader(file)
|
||||
for {
|
||||
offset++
|
||||
line, err := reader.ReadBytes(byte('\n'))
|
||||
if err == io.EOF {
|
||||
break
|
||||
} else if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
entry, err := NewEntry(line)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
entry.Offset = offset
|
||||
s.Entries[entry.Index] = entry
|
||||
s.Index = append(s.Index, &entry.Index)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// PrintEntryAddress prints the memory addresses in hex (0x...) format of the
|
||||
// entries for the list of given indexes.
|
||||
func (s *Dump) PrintEntryAddress(indexes []*string) {
|
||||
for _, index := range indexes {
|
||||
if entry, ok := s.Entries[*index]; ok {
|
||||
fmt.Println(entry.Address())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// PrintEntryJSON prints the full JSON blob from the input file for the entries
|
||||
// with the given indexes. It does this by using the Offset value of the entries
|
||||
// to avoid having to load up the whole dump file in memory.
|
||||
func (s *Dump) PrintEntryJSON(indexes []*string) error {
|
||||
file, err := os.Open(s.FilePath)
|
||||
defer file.Close()
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
offsets := s.sortedOffsets(indexes)
|
||||
offsetsLength := int64(len(offsets))
|
||||
|
||||
if offsetsLength == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
var current int64
|
||||
var offset int64 = -1
|
||||
reader := bufio.NewReader(file)
|
||||
for {
|
||||
offset++
|
||||
line, err := reader.ReadBytes(byte('\n'))
|
||||
if err == io.EOF {
|
||||
break
|
||||
} else if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if offset == offsets[current] {
|
||||
current++
|
||||
fmt.Print(string(line))
|
||||
}
|
||||
|
||||
if current >= offsetsLength-1 {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Dump) sortedOffsets(indexes []*string) []int64 {
|
||||
var res []int64
|
||||
|
||||
for _, index := range indexes {
|
||||
res = append(res, s.Entries[*index].Offset)
|
||||
}
|
||||
sort.Slice(res, func(i, j int) bool { return res[i] < res[j] })
|
||||
|
||||
return res
|
||||
}
|
||||
27
leak/entry.go
Normal file
27
leak/entry.go
Normal file
@@ -0,0 +1,27 @@
|
||||
package leak
|
||||
|
||||
// NewEntry returns a new *Entry instance initialized with a *Object of the
|
||||
// given input JSON data.
|
||||
func NewEntry(inputJSON []byte) (*Entry, error) {
|
||||
obj, err := NewObject(inputJSON)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &Entry{
|
||||
Object: obj,
|
||||
Index: obj.Index(),
|
||||
}, err
|
||||
}
|
||||
|
||||
// Entry is a parsed heap item object
|
||||
type Entry struct {
|
||||
Object *Object
|
||||
Offset int64
|
||||
Index string
|
||||
}
|
||||
|
||||
// Address returns the Address property of the entry's Object.
|
||||
func (s *Entry) Address() string {
|
||||
return s.Object.Address
|
||||
}
|
||||
@@ -3,8 +3,6 @@ package leak
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/jimeh/rbheap/obj"
|
||||
)
|
||||
|
||||
// NewFinder returns a new *Finder instance, populated with the three given file
|
||||
@@ -19,7 +17,7 @@ func NewFinder(filePath1, filePath2, filePath3 string) *Finder {
|
||||
// from a Ruby process.
|
||||
type Finder struct {
|
||||
FilePaths [3]string
|
||||
Dumps [3]*obj.Dump
|
||||
Dumps [3]*Dump
|
||||
Leaks []*string
|
||||
Verbose bool
|
||||
}
|
||||
@@ -29,7 +27,7 @@ func (s *Finder) Process() error {
|
||||
for i, filePath := range s.FilePaths {
|
||||
start := time.Now()
|
||||
s.log(fmt.Sprintf("Parsing %s", filePath))
|
||||
dump := obj.NewDump(filePath)
|
||||
dump := NewDump(filePath)
|
||||
|
||||
err := dump.Process()
|
||||
if err != nil {
|
||||
|
||||
26
leak/object.go
Normal file
26
leak/object.go
Normal file
@@ -0,0 +1,26 @@
|
||||
package leak
|
||||
|
||||
import "encoding/json"
|
||||
|
||||
//go:generate easyjson -all object.go
|
||||
|
||||
// NewObject returns a new *Object instance with it's attributes populated from
|
||||
// the given input JSON data.
|
||||
func NewObject(inputJSON []byte) (*Object, error) {
|
||||
var obj Object
|
||||
err := json.Unmarshal(inputJSON, &obj)
|
||||
|
||||
return &obj, err
|
||||
}
|
||||
|
||||
// Object is a minimal representation of a Ruby heap object as exported from
|
||||
// Ruby via `ObjectSpace.dump_all`.
|
||||
type Object struct {
|
||||
Address string `json:"address"`
|
||||
Type string `json:"type"`
|
||||
}
|
||||
|
||||
// Index returns a unique index for the given Object.
|
||||
func (s *Object) Index() string {
|
||||
return s.Address + ":" + s.Type
|
||||
}
|
||||
102
leak/object_easyjson.go
Normal file
102
leak/object_easyjson.go
Normal file
@@ -0,0 +1,102 @@
|
||||
// Code generated by easyjson for marshaling/unmarshaling. DO NOT EDIT.
|
||||
|
||||
package leak
|
||||
|
||||
import (
|
||||
json "encoding/json"
|
||||
easyjson "github.com/mailru/easyjson"
|
||||
jlexer "github.com/mailru/easyjson/jlexer"
|
||||
jwriter "github.com/mailru/easyjson/jwriter"
|
||||
)
|
||||
|
||||
// suppress unused package warning
|
||||
var (
|
||||
_ *json.RawMessage
|
||||
_ *jlexer.Lexer
|
||||
_ *jwriter.Writer
|
||||
_ easyjson.Marshaler
|
||||
)
|
||||
|
||||
func easyjsonE44bcf2dDecodeGithubComJimehRbheapLeak(in *jlexer.Lexer, out *Object) {
|
||||
isTopLevel := in.IsStart()
|
||||
if in.IsNull() {
|
||||
if isTopLevel {
|
||||
in.Consumed()
|
||||
}
|
||||
in.Skip()
|
||||
return
|
||||
}
|
||||
in.Delim('{')
|
||||
for !in.IsDelim('}') {
|
||||
key := in.UnsafeString()
|
||||
in.WantColon()
|
||||
if in.IsNull() {
|
||||
in.Skip()
|
||||
in.WantComma()
|
||||
continue
|
||||
}
|
||||
switch key {
|
||||
case "address":
|
||||
out.Address = string(in.String())
|
||||
case "type":
|
||||
out.Type = string(in.String())
|
||||
default:
|
||||
in.SkipRecursive()
|
||||
}
|
||||
in.WantComma()
|
||||
}
|
||||
in.Delim('}')
|
||||
if isTopLevel {
|
||||
in.Consumed()
|
||||
}
|
||||
}
|
||||
func easyjsonE44bcf2dEncodeGithubComJimehRbheapLeak(out *jwriter.Writer, in Object) {
|
||||
out.RawByte('{')
|
||||
first := true
|
||||
_ = first
|
||||
{
|
||||
const prefix string = ",\"address\":"
|
||||
if first {
|
||||
first = false
|
||||
out.RawString(prefix[1:])
|
||||
} else {
|
||||
out.RawString(prefix)
|
||||
}
|
||||
out.String(string(in.Address))
|
||||
}
|
||||
{
|
||||
const prefix string = ",\"type\":"
|
||||
if first {
|
||||
first = false
|
||||
out.RawString(prefix[1:])
|
||||
} else {
|
||||
out.RawString(prefix)
|
||||
}
|
||||
out.String(string(in.Type))
|
||||
}
|
||||
out.RawByte('}')
|
||||
}
|
||||
|
||||
// MarshalJSON supports json.Marshaler interface
|
||||
func (v Object) MarshalJSON() ([]byte, error) {
|
||||
w := jwriter.Writer{}
|
||||
easyjsonE44bcf2dEncodeGithubComJimehRbheapLeak(&w, v)
|
||||
return w.Buffer.BuildBytes(), w.Error
|
||||
}
|
||||
|
||||
// MarshalEasyJSON supports easyjson.Marshaler interface
|
||||
func (v Object) MarshalEasyJSON(w *jwriter.Writer) {
|
||||
easyjsonE44bcf2dEncodeGithubComJimehRbheapLeak(w, v)
|
||||
}
|
||||
|
||||
// UnmarshalJSON supports json.Unmarshaler interface
|
||||
func (v *Object) UnmarshalJSON(data []byte) error {
|
||||
r := jlexer.Lexer{Data: data}
|
||||
easyjsonE44bcf2dDecodeGithubComJimehRbheapLeak(&r, v)
|
||||
return r.Error()
|
||||
}
|
||||
|
||||
// UnmarshalEasyJSON supports easyjson.Unmarshaler interface
|
||||
func (v *Object) UnmarshalEasyJSON(l *jlexer.Lexer) {
|
||||
easyjsonE44bcf2dDecodeGithubComJimehRbheapLeak(l, v)
|
||||
}
|
||||
Reference in New Issue
Block a user