mirror of
https://github.com/jimeh/rbheap.git
synced 2026-02-19 12:56:46 +00:00
Initial hacky version of inspect source command
This commit is contained in:
@@ -1,39 +1,25 @@
|
|||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/jimeh/rbheap/inspect"
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
// inspectCmd represents the inspect command
|
// inspectCmd represents the inspect command
|
||||||
var inspectCmd = &cobra.Command{
|
var inspectCmd = &cobra.Command{
|
||||||
Use: "inspect [flags] <dump-file>",
|
Use: "inspect",
|
||||||
Short: "Inspect ObjectSpace dumps from Ruby proceees",
|
Short: "Inspect ObjectSpace dumps from Ruby proceees",
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
|
||||||
if len(args) != 1 {
|
|
||||||
usage_er(cmd, fmt.Sprintf("requires 1 arg, received %d", len(args)))
|
|
||||||
}
|
|
||||||
|
|
||||||
inspector := inspect.New(args[0])
|
|
||||||
inspector.Verbose = inspectOpts.Verbose
|
|
||||||
inspector.Process()
|
|
||||||
|
|
||||||
inspector.PrintCountByFileAndLines()
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var inspectOpts = struct {
|
var inspectOpts = struct {
|
||||||
Verbose bool
|
Verbose bool
|
||||||
|
Output string
|
||||||
}{}
|
}{}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
rootCmd.AddCommand(inspectCmd)
|
rootCmd.AddCommand(inspectCmd)
|
||||||
|
|
||||||
inspectCmd.PersistentFlags().BoolVarP(
|
inspectCmd.PersistentFlags().BoolVarP(&inspectOpts.Verbose,
|
||||||
&inspectOpts.Verbose,
|
|
||||||
"verbose", "v", false,
|
"verbose", "v", false,
|
||||||
"print verbose information",
|
"print verbose information to STDERR",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
60
cmd/inspectSource.go
Normal file
60
cmd/inspectSource.go
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/jimeh/rbheap/inspect"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
// inspectSourceCmd represents the inspectSource command
|
||||||
|
var inspectSourceCmd = &cobra.Command{
|
||||||
|
Use: "source [flags] <dump-file>",
|
||||||
|
Short: "Group objects by source filename and line number",
|
||||||
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
|
if len(args) != 1 {
|
||||||
|
usage_er(cmd, fmt.Sprintf("requires 1 arg, received %d", len(args)))
|
||||||
|
}
|
||||||
|
|
||||||
|
inspector := inspect.NewSourceInspector(args[0])
|
||||||
|
inspector.Verbose = inspectOpts.Verbose
|
||||||
|
inspector.SortBy = inspectSourceOpts.SortBy
|
||||||
|
inspector.Limit = inspectSourceOpts.Limit
|
||||||
|
inspector.Load()
|
||||||
|
|
||||||
|
switch inspectSourceOpts.Breakdown {
|
||||||
|
case "file":
|
||||||
|
inspector.ByFile(os.Stdout)
|
||||||
|
case "line":
|
||||||
|
inspector.ByLine(os.Stdout)
|
||||||
|
default:
|
||||||
|
usage_er(cmd, "Invalid --breakdown option")
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
var inspectSourceOpts = struct {
|
||||||
|
Breakdown string
|
||||||
|
SortBy string
|
||||||
|
Limit int
|
||||||
|
}{}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
inspectCmd.AddCommand(inspectSourceCmd)
|
||||||
|
|
||||||
|
inspectSourceCmd.Flags().StringVarP(&inspectSourceOpts.Breakdown,
|
||||||
|
"breakdown", "b", "line",
|
||||||
|
"Breakdown sources by \"line\" or \"file\"",
|
||||||
|
)
|
||||||
|
|
||||||
|
inspectSourceCmd.Flags().StringVarP(&inspectSourceOpts.SortBy,
|
||||||
|
"sort", "s", "count",
|
||||||
|
"Sort by \"count\", \"memsize\", or \"bytesize\"",
|
||||||
|
)
|
||||||
|
|
||||||
|
inspectSourceCmd.Flags().IntVarP(&inspectSourceOpts.Limit,
|
||||||
|
"limit", "l", 0,
|
||||||
|
"Limit number of results to show",
|
||||||
|
)
|
||||||
|
}
|
||||||
52
inspect/base_inspector.go
Normal file
52
inspect/base_inspector.go
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
package inspect
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type BaseInspector struct {
|
||||||
|
FilePath string
|
||||||
|
Dump *Dump
|
||||||
|
Verbose bool
|
||||||
|
VerboseWriter io.Writer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SourceInspector) Load() error {
|
||||||
|
start := time.Now()
|
||||||
|
s.verbose(fmt.Sprintf("Loading %s...", s.FilePath))
|
||||||
|
|
||||||
|
err := s.Dump.Load()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
elapsed := time.Now().Sub(start)
|
||||||
|
s.verbose(fmt.Sprintf(
|
||||||
|
"Loaded %d objects in %.6f seconds",
|
||||||
|
len(s.Dump.Objects),
|
||||||
|
elapsed.Seconds(),
|
||||||
|
))
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *BaseInspector) log(msg string) {
|
||||||
|
if s.Verbose {
|
||||||
|
fmt.Println(msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *BaseInspector) verbose(msg string) {
|
||||||
|
if s.Verbose {
|
||||||
|
w := s.VerboseWriter
|
||||||
|
|
||||||
|
if w == nil {
|
||||||
|
w = os.Stderr
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Fprintln(w, msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,7 +4,6 @@ import (
|
|||||||
"bufio"
|
"bufio"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"strconv"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewDump(filePath string) *Dump {
|
func NewDump(filePath string) *Dump {
|
||||||
@@ -14,14 +13,14 @@ func NewDump(filePath string) *Dump {
|
|||||||
// Dump contains all relevant data for a single heap dump.
|
// Dump contains all relevant data for a single heap dump.
|
||||||
type Dump struct {
|
type Dump struct {
|
||||||
FilePath string
|
FilePath string
|
||||||
ByAddress map[string]*Object
|
Objects map[string]*Object
|
||||||
ByFile map[string][]*Object
|
ByFile map[string][]*Object
|
||||||
ByFileAndLine map[string][]*Object
|
ByFileAndLine map[string][]*Object
|
||||||
ByGeneration map[int][]*Object
|
ByGeneration map[int][]*Object
|
||||||
}
|
}
|
||||||
|
|
||||||
// Process processes the heap dump referenced in FilePath.
|
// Load processes the heap dump referenced in FilePath.
|
||||||
func (s *Dump) Process() error {
|
func (s *Dump) Load() error {
|
||||||
file, err := os.Open(s.FilePath)
|
file, err := os.Open(s.FilePath)
|
||||||
defer file.Close()
|
defer file.Close()
|
||||||
|
|
||||||
@@ -29,10 +28,7 @@ func (s *Dump) Process() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
s.ByAddress = map[string]*Object{}
|
s.Objects = map[string]*Object{}
|
||||||
s.ByFile = map[string][]*Object{}
|
|
||||||
s.ByFileAndLine = map[string][]*Object{}
|
|
||||||
s.ByGeneration = map[int][]*Object{}
|
|
||||||
|
|
||||||
reader := bufio.NewReader(file)
|
reader := bufio.NewReader(file)
|
||||||
for {
|
for {
|
||||||
@@ -54,21 +50,12 @@ func (s *Dump) Process() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Dump) Lookup(address string) (*Object, bool) {
|
||||||
|
object, ok := s.Objects[address]
|
||||||
|
return object, ok
|
||||||
|
}
|
||||||
|
|
||||||
// AddObject adds a *Object to the Dump.
|
// AddObject adds a *Object to the Dump.
|
||||||
func (s *Dump) AddObject(obj *Object) {
|
func (s *Dump) AddObject(obj *Object) {
|
||||||
s.ByAddress[obj.Address] = obj
|
s.Objects[obj.Address] = obj
|
||||||
|
|
||||||
if obj.File != "" {
|
|
||||||
s.ByFile[obj.File] = append(s.ByFile[obj.File], obj)
|
|
||||||
}
|
|
||||||
|
|
||||||
if obj.File != "" && obj.Line != 0 {
|
|
||||||
key := obj.File + ":" + strconv.Itoa(obj.Line)
|
|
||||||
s.ByFileAndLine[key] = append(s.ByFileAndLine[key], obj)
|
|
||||||
}
|
|
||||||
|
|
||||||
if obj.Generation != 0 {
|
|
||||||
s.ByGeneration[obj.Generation] =
|
|
||||||
append(s.ByGeneration[obj.Generation], obj)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
30
inspect/file.go
Normal file
30
inspect/file.go
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
package inspect
|
||||||
|
|
||||||
|
// NewFile creates a new File.
|
||||||
|
func NewFile(filePath string) *File {
|
||||||
|
return &File{
|
||||||
|
FilePath: filePath,
|
||||||
|
ObjectMap: map[string]*Object{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// File represents a source file and the lines and objects allocated by them.
|
||||||
|
type File struct {
|
||||||
|
FilePath string
|
||||||
|
ObjectMap map[string]*Object
|
||||||
|
ObjectCount int
|
||||||
|
ByteSize int64
|
||||||
|
MemSize int64
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add adds a object to a File.
|
||||||
|
func (s *File) Add(obj *Object) {
|
||||||
|
_, ok := s.ObjectMap[obj.Address]
|
||||||
|
if !ok && obj.File != "" && obj.Line != 0 {
|
||||||
|
s.ObjectCount++
|
||||||
|
s.ByteSize = s.ByteSize + obj.ByteSize
|
||||||
|
s.MemSize = s.MemSize + obj.MemSize
|
||||||
|
|
||||||
|
s.ObjectMap[obj.Address] = obj
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
package inspect
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
func New(filePath string) *Inspector {
|
|
||||||
|
|
||||||
return &Inspector{
|
|
||||||
FilePath: filePath,
|
|
||||||
Dump: NewDump(filePath),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type Inspector struct {
|
|
||||||
FilePath string
|
|
||||||
Dump *Dump
|
|
||||||
Verbose bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Inspector) Process() {
|
|
||||||
|
|
||||||
start := time.Now()
|
|
||||||
s.log(fmt.Sprintf("Parsing %s", s.FilePath))
|
|
||||||
|
|
||||||
s.Dump.Process()
|
|
||||||
|
|
||||||
elapsed := time.Now().Sub(start)
|
|
||||||
s.log(fmt.Sprintf(
|
|
||||||
"Parsed %d objects in %.6f seconds",
|
|
||||||
len(s.Dump.ByAddress),
|
|
||||||
elapsed.Seconds(),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Inspector) PrintCountByFileAndLines() {
|
|
||||||
for k, objects := range s.Dump.ByFileAndLine {
|
|
||||||
fmt.Printf("%s: %d objects\n", k, len(objects))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Inspector) log(msg string) {
|
|
||||||
if s.Verbose {
|
|
||||||
fmt.Println(msg)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
32
inspect/line.go
Normal file
32
inspect/line.go
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
package inspect
|
||||||
|
|
||||||
|
// NewLine creates a new Line.
|
||||||
|
func NewLine(filePath string, lineNum int) *Line {
|
||||||
|
return &Line{
|
||||||
|
FilePath: filePath,
|
||||||
|
LineNum: lineNum,
|
||||||
|
ObjectMap: map[string]*Object{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Line represents a source line within a file and the objects allocated by it.
|
||||||
|
type Line struct {
|
||||||
|
FilePath string
|
||||||
|
LineNum int
|
||||||
|
ObjectMap map[string]*Object
|
||||||
|
ObjectCount int
|
||||||
|
ByteSize int64
|
||||||
|
MemSize int64
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add adds a Object to a Line.
|
||||||
|
func (s *Line) Add(obj *Object) {
|
||||||
|
_, ok := s.ObjectMap[obj.Address]
|
||||||
|
if !ok && obj.File != "" && obj.Line != 0 {
|
||||||
|
s.ObjectCount++
|
||||||
|
s.ByteSize = s.ByteSize + obj.ByteSize
|
||||||
|
s.MemSize = s.MemSize + obj.MemSize
|
||||||
|
|
||||||
|
s.ObjectMap[obj.Address] = obj
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -17,7 +17,7 @@ func NewObject(inputJSON []byte) (*Object, error) {
|
|||||||
// `ObjectSpace.dump_all`.
|
// `ObjectSpace.dump_all`.
|
||||||
type Object struct {
|
type Object struct {
|
||||||
Address string `json:"address"`
|
Address string `json:"address"`
|
||||||
Bytesize int `json:"bytesize"`
|
ByteSize int64 `json:"bytesize"`
|
||||||
Capacity int `json:"capacity"`
|
Capacity int `json:"capacity"`
|
||||||
Class string `json:"class"`
|
Class string `json:"class"`
|
||||||
Default string `json:"default"`
|
Default string `json:"default"`
|
||||||
@@ -31,15 +31,15 @@ type Object struct {
|
|||||||
Generation int `json:"generation"`
|
Generation int `json:"generation"`
|
||||||
ImemoType string `json:"imemo_type"`
|
ImemoType string `json:"imemo_type"`
|
||||||
Ivars int `json:"ivars"`
|
Ivars int `json:"ivars"`
|
||||||
Length int `json:"length"`
|
Length int64 `json:"length"`
|
||||||
Line int `json:"line"`
|
Line int `json:"line"`
|
||||||
Memsize int `json:"memsize"`
|
MemSize int64 `json:"memsize"`
|
||||||
Method string `json:"method"`
|
Method string `json:"method"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
References ObjectReferences `json:"references"`
|
References ObjectReferences `json:"references"`
|
||||||
Root string `json:"root"`
|
Root string `json:"root"`
|
||||||
Shared bool `json:"shared"`
|
Shared bool `json:"shared"`
|
||||||
Size int `json:"size"`
|
Size int64 `json:"size"`
|
||||||
Struct string `json:"struct"`
|
Struct string `json:"struct"`
|
||||||
Type string `json:"type"`
|
Type string `json:"type"`
|
||||||
Value string `json:"value"`
|
Value string `json:"value"`
|
||||||
|
|||||||
@@ -1,543 +0,0 @@
|
|||||||
// Code generated by easyjson for marshaling/unmarshaling. DO NOT EDIT.
|
|
||||||
|
|
||||||
package inspect
|
|
||||||
|
|
||||||
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 easyjsonE44bcf2dDecodeGithubComJimehRbheapInspect(in *jlexer.Lexer, out *ObjectFlags) {
|
|
||||||
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 "marked":
|
|
||||||
out.Marked = bool(in.Bool())
|
|
||||||
case "old":
|
|
||||||
out.Old = bool(in.Bool())
|
|
||||||
case "uncollectible":
|
|
||||||
out.Uncollectible = bool(in.Bool())
|
|
||||||
case "wb_protected":
|
|
||||||
out.WbProtected = bool(in.Bool())
|
|
||||||
default:
|
|
||||||
in.SkipRecursive()
|
|
||||||
}
|
|
||||||
in.WantComma()
|
|
||||||
}
|
|
||||||
in.Delim('}')
|
|
||||||
if isTopLevel {
|
|
||||||
in.Consumed()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
func easyjsonE44bcf2dEncodeGithubComJimehRbheapInspect(out *jwriter.Writer, in ObjectFlags) {
|
|
||||||
out.RawByte('{')
|
|
||||||
first := true
|
|
||||||
_ = first
|
|
||||||
{
|
|
||||||
const prefix string = ",\"marked\":"
|
|
||||||
if first {
|
|
||||||
first = false
|
|
||||||
out.RawString(prefix[1:])
|
|
||||||
} else {
|
|
||||||
out.RawString(prefix)
|
|
||||||
}
|
|
||||||
out.Bool(bool(in.Marked))
|
|
||||||
}
|
|
||||||
{
|
|
||||||
const prefix string = ",\"old\":"
|
|
||||||
if first {
|
|
||||||
first = false
|
|
||||||
out.RawString(prefix[1:])
|
|
||||||
} else {
|
|
||||||
out.RawString(prefix)
|
|
||||||
}
|
|
||||||
out.Bool(bool(in.Old))
|
|
||||||
}
|
|
||||||
{
|
|
||||||
const prefix string = ",\"uncollectible\":"
|
|
||||||
if first {
|
|
||||||
first = false
|
|
||||||
out.RawString(prefix[1:])
|
|
||||||
} else {
|
|
||||||
out.RawString(prefix)
|
|
||||||
}
|
|
||||||
out.Bool(bool(in.Uncollectible))
|
|
||||||
}
|
|
||||||
{
|
|
||||||
const prefix string = ",\"wb_protected\":"
|
|
||||||
if first {
|
|
||||||
first = false
|
|
||||||
out.RawString(prefix[1:])
|
|
||||||
} else {
|
|
||||||
out.RawString(prefix)
|
|
||||||
}
|
|
||||||
out.Bool(bool(in.WbProtected))
|
|
||||||
}
|
|
||||||
out.RawByte('}')
|
|
||||||
}
|
|
||||||
|
|
||||||
// MarshalJSON supports json.Marshaler interface
|
|
||||||
func (v ObjectFlags) MarshalJSON() ([]byte, error) {
|
|
||||||
w := jwriter.Writer{}
|
|
||||||
easyjsonE44bcf2dEncodeGithubComJimehRbheapInspect(&w, v)
|
|
||||||
return w.Buffer.BuildBytes(), w.Error
|
|
||||||
}
|
|
||||||
|
|
||||||
// MarshalEasyJSON supports easyjson.Marshaler interface
|
|
||||||
func (v ObjectFlags) MarshalEasyJSON(w *jwriter.Writer) {
|
|
||||||
easyjsonE44bcf2dEncodeGithubComJimehRbheapInspect(w, v)
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnmarshalJSON supports json.Unmarshaler interface
|
|
||||||
func (v *ObjectFlags) UnmarshalJSON(data []byte) error {
|
|
||||||
r := jlexer.Lexer{Data: data}
|
|
||||||
easyjsonE44bcf2dDecodeGithubComJimehRbheapInspect(&r, v)
|
|
||||||
return r.Error()
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnmarshalEasyJSON supports easyjson.Unmarshaler interface
|
|
||||||
func (v *ObjectFlags) UnmarshalEasyJSON(l *jlexer.Lexer) {
|
|
||||||
easyjsonE44bcf2dDecodeGithubComJimehRbheapInspect(l, v)
|
|
||||||
}
|
|
||||||
func easyjsonE44bcf2dDecodeGithubComJimehRbheapInspect1(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 "bytesize":
|
|
||||||
out.Bytesize = int(in.Int())
|
|
||||||
case "capacity":
|
|
||||||
out.Capacity = int(in.Int())
|
|
||||||
case "class":
|
|
||||||
out.Class = string(in.String())
|
|
||||||
case "default":
|
|
||||||
out.Default = string(in.String())
|
|
||||||
case "embedded":
|
|
||||||
out.Embedded = bool(in.Bool())
|
|
||||||
case "encoding":
|
|
||||||
out.Encoding = string(in.String())
|
|
||||||
case "fd":
|
|
||||||
out.Fd = int(in.Int())
|
|
||||||
case "file":
|
|
||||||
out.File = string(in.String())
|
|
||||||
case "flags":
|
|
||||||
if data := in.Raw(); in.Ok() {
|
|
||||||
in.AddError((out.Flags).UnmarshalJSON(data))
|
|
||||||
}
|
|
||||||
case "frozen":
|
|
||||||
out.Frozen = bool(in.Bool())
|
|
||||||
case "fstring":
|
|
||||||
out.Fstring = bool(in.Bool())
|
|
||||||
case "generation":
|
|
||||||
out.Generation = int(in.Int())
|
|
||||||
case "imemo_type":
|
|
||||||
out.ImemoType = string(in.String())
|
|
||||||
case "ivars":
|
|
||||||
out.Ivars = int(in.Int())
|
|
||||||
case "length":
|
|
||||||
out.Length = int(in.Int())
|
|
||||||
case "line":
|
|
||||||
out.Line = int(in.Int())
|
|
||||||
case "memsize":
|
|
||||||
out.Memsize = int(in.Int())
|
|
||||||
case "method":
|
|
||||||
out.Method = string(in.String())
|
|
||||||
case "name":
|
|
||||||
out.Name = string(in.String())
|
|
||||||
case "references":
|
|
||||||
if in.IsNull() {
|
|
||||||
in.Skip()
|
|
||||||
out.References = nil
|
|
||||||
} else {
|
|
||||||
in.Delim('[')
|
|
||||||
if out.References == nil {
|
|
||||||
if !in.IsDelim(']') {
|
|
||||||
out.References = make(ObjectReferences, 0, 4)
|
|
||||||
} else {
|
|
||||||
out.References = ObjectReferences{}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
out.References = (out.References)[:0]
|
|
||||||
}
|
|
||||||
for !in.IsDelim(']') {
|
|
||||||
var v1 string
|
|
||||||
v1 = string(in.String())
|
|
||||||
out.References = append(out.References, v1)
|
|
||||||
in.WantComma()
|
|
||||||
}
|
|
||||||
in.Delim(']')
|
|
||||||
}
|
|
||||||
case "root":
|
|
||||||
out.Root = string(in.String())
|
|
||||||
case "shared":
|
|
||||||
out.Shared = bool(in.Bool())
|
|
||||||
case "size":
|
|
||||||
out.Size = int(in.Int())
|
|
||||||
case "struct":
|
|
||||||
out.Struct = string(in.String())
|
|
||||||
case "type":
|
|
||||||
out.Type = string(in.String())
|
|
||||||
case "value":
|
|
||||||
out.Value = string(in.String())
|
|
||||||
default:
|
|
||||||
in.SkipRecursive()
|
|
||||||
}
|
|
||||||
in.WantComma()
|
|
||||||
}
|
|
||||||
in.Delim('}')
|
|
||||||
if isTopLevel {
|
|
||||||
in.Consumed()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
func easyjsonE44bcf2dEncodeGithubComJimehRbheapInspect1(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 = ",\"bytesize\":"
|
|
||||||
if first {
|
|
||||||
first = false
|
|
||||||
out.RawString(prefix[1:])
|
|
||||||
} else {
|
|
||||||
out.RawString(prefix)
|
|
||||||
}
|
|
||||||
out.Int(int(in.Bytesize))
|
|
||||||
}
|
|
||||||
{
|
|
||||||
const prefix string = ",\"capacity\":"
|
|
||||||
if first {
|
|
||||||
first = false
|
|
||||||
out.RawString(prefix[1:])
|
|
||||||
} else {
|
|
||||||
out.RawString(prefix)
|
|
||||||
}
|
|
||||||
out.Int(int(in.Capacity))
|
|
||||||
}
|
|
||||||
{
|
|
||||||
const prefix string = ",\"class\":"
|
|
||||||
if first {
|
|
||||||
first = false
|
|
||||||
out.RawString(prefix[1:])
|
|
||||||
} else {
|
|
||||||
out.RawString(prefix)
|
|
||||||
}
|
|
||||||
out.String(string(in.Class))
|
|
||||||
}
|
|
||||||
{
|
|
||||||
const prefix string = ",\"default\":"
|
|
||||||
if first {
|
|
||||||
first = false
|
|
||||||
out.RawString(prefix[1:])
|
|
||||||
} else {
|
|
||||||
out.RawString(prefix)
|
|
||||||
}
|
|
||||||
out.String(string(in.Default))
|
|
||||||
}
|
|
||||||
{
|
|
||||||
const prefix string = ",\"embedded\":"
|
|
||||||
if first {
|
|
||||||
first = false
|
|
||||||
out.RawString(prefix[1:])
|
|
||||||
} else {
|
|
||||||
out.RawString(prefix)
|
|
||||||
}
|
|
||||||
out.Bool(bool(in.Embedded))
|
|
||||||
}
|
|
||||||
{
|
|
||||||
const prefix string = ",\"encoding\":"
|
|
||||||
if first {
|
|
||||||
first = false
|
|
||||||
out.RawString(prefix[1:])
|
|
||||||
} else {
|
|
||||||
out.RawString(prefix)
|
|
||||||
}
|
|
||||||
out.String(string(in.Encoding))
|
|
||||||
}
|
|
||||||
{
|
|
||||||
const prefix string = ",\"fd\":"
|
|
||||||
if first {
|
|
||||||
first = false
|
|
||||||
out.RawString(prefix[1:])
|
|
||||||
} else {
|
|
||||||
out.RawString(prefix)
|
|
||||||
}
|
|
||||||
out.Int(int(in.Fd))
|
|
||||||
}
|
|
||||||
{
|
|
||||||
const prefix string = ",\"file\":"
|
|
||||||
if first {
|
|
||||||
first = false
|
|
||||||
out.RawString(prefix[1:])
|
|
||||||
} else {
|
|
||||||
out.RawString(prefix)
|
|
||||||
}
|
|
||||||
out.String(string(in.File))
|
|
||||||
}
|
|
||||||
{
|
|
||||||
const prefix string = ",\"flags\":"
|
|
||||||
if first {
|
|
||||||
first = false
|
|
||||||
out.RawString(prefix[1:])
|
|
||||||
} else {
|
|
||||||
out.RawString(prefix)
|
|
||||||
}
|
|
||||||
out.Raw((in.Flags).MarshalJSON())
|
|
||||||
}
|
|
||||||
{
|
|
||||||
const prefix string = ",\"frozen\":"
|
|
||||||
if first {
|
|
||||||
first = false
|
|
||||||
out.RawString(prefix[1:])
|
|
||||||
} else {
|
|
||||||
out.RawString(prefix)
|
|
||||||
}
|
|
||||||
out.Bool(bool(in.Frozen))
|
|
||||||
}
|
|
||||||
{
|
|
||||||
const prefix string = ",\"fstring\":"
|
|
||||||
if first {
|
|
||||||
first = false
|
|
||||||
out.RawString(prefix[1:])
|
|
||||||
} else {
|
|
||||||
out.RawString(prefix)
|
|
||||||
}
|
|
||||||
out.Bool(bool(in.Fstring))
|
|
||||||
}
|
|
||||||
{
|
|
||||||
const prefix string = ",\"generation\":"
|
|
||||||
if first {
|
|
||||||
first = false
|
|
||||||
out.RawString(prefix[1:])
|
|
||||||
} else {
|
|
||||||
out.RawString(prefix)
|
|
||||||
}
|
|
||||||
out.Int(int(in.Generation))
|
|
||||||
}
|
|
||||||
{
|
|
||||||
const prefix string = ",\"imemo_type\":"
|
|
||||||
if first {
|
|
||||||
first = false
|
|
||||||
out.RawString(prefix[1:])
|
|
||||||
} else {
|
|
||||||
out.RawString(prefix)
|
|
||||||
}
|
|
||||||
out.String(string(in.ImemoType))
|
|
||||||
}
|
|
||||||
{
|
|
||||||
const prefix string = ",\"ivars\":"
|
|
||||||
if first {
|
|
||||||
first = false
|
|
||||||
out.RawString(prefix[1:])
|
|
||||||
} else {
|
|
||||||
out.RawString(prefix)
|
|
||||||
}
|
|
||||||
out.Int(int(in.Ivars))
|
|
||||||
}
|
|
||||||
{
|
|
||||||
const prefix string = ",\"length\":"
|
|
||||||
if first {
|
|
||||||
first = false
|
|
||||||
out.RawString(prefix[1:])
|
|
||||||
} else {
|
|
||||||
out.RawString(prefix)
|
|
||||||
}
|
|
||||||
out.Int(int(in.Length))
|
|
||||||
}
|
|
||||||
{
|
|
||||||
const prefix string = ",\"line\":"
|
|
||||||
if first {
|
|
||||||
first = false
|
|
||||||
out.RawString(prefix[1:])
|
|
||||||
} else {
|
|
||||||
out.RawString(prefix)
|
|
||||||
}
|
|
||||||
out.Int(int(in.Line))
|
|
||||||
}
|
|
||||||
{
|
|
||||||
const prefix string = ",\"memsize\":"
|
|
||||||
if first {
|
|
||||||
first = false
|
|
||||||
out.RawString(prefix[1:])
|
|
||||||
} else {
|
|
||||||
out.RawString(prefix)
|
|
||||||
}
|
|
||||||
out.Int(int(in.Memsize))
|
|
||||||
}
|
|
||||||
{
|
|
||||||
const prefix string = ",\"method\":"
|
|
||||||
if first {
|
|
||||||
first = false
|
|
||||||
out.RawString(prefix[1:])
|
|
||||||
} else {
|
|
||||||
out.RawString(prefix)
|
|
||||||
}
|
|
||||||
out.String(string(in.Method))
|
|
||||||
}
|
|
||||||
{
|
|
||||||
const prefix string = ",\"name\":"
|
|
||||||
if first {
|
|
||||||
first = false
|
|
||||||
out.RawString(prefix[1:])
|
|
||||||
} else {
|
|
||||||
out.RawString(prefix)
|
|
||||||
}
|
|
||||||
out.String(string(in.Name))
|
|
||||||
}
|
|
||||||
{
|
|
||||||
const prefix string = ",\"references\":"
|
|
||||||
if first {
|
|
||||||
first = false
|
|
||||||
out.RawString(prefix[1:])
|
|
||||||
} else {
|
|
||||||
out.RawString(prefix)
|
|
||||||
}
|
|
||||||
if in.References == nil && (out.Flags&jwriter.NilSliceAsEmpty) == 0 {
|
|
||||||
out.RawString("null")
|
|
||||||
} else {
|
|
||||||
out.RawByte('[')
|
|
||||||
for v2, v3 := range in.References {
|
|
||||||
if v2 > 0 {
|
|
||||||
out.RawByte(',')
|
|
||||||
}
|
|
||||||
out.String(string(v3))
|
|
||||||
}
|
|
||||||
out.RawByte(']')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
{
|
|
||||||
const prefix string = ",\"root\":"
|
|
||||||
if first {
|
|
||||||
first = false
|
|
||||||
out.RawString(prefix[1:])
|
|
||||||
} else {
|
|
||||||
out.RawString(prefix)
|
|
||||||
}
|
|
||||||
out.String(string(in.Root))
|
|
||||||
}
|
|
||||||
{
|
|
||||||
const prefix string = ",\"shared\":"
|
|
||||||
if first {
|
|
||||||
first = false
|
|
||||||
out.RawString(prefix[1:])
|
|
||||||
} else {
|
|
||||||
out.RawString(prefix)
|
|
||||||
}
|
|
||||||
out.Bool(bool(in.Shared))
|
|
||||||
}
|
|
||||||
{
|
|
||||||
const prefix string = ",\"size\":"
|
|
||||||
if first {
|
|
||||||
first = false
|
|
||||||
out.RawString(prefix[1:])
|
|
||||||
} else {
|
|
||||||
out.RawString(prefix)
|
|
||||||
}
|
|
||||||
out.Int(int(in.Size))
|
|
||||||
}
|
|
||||||
{
|
|
||||||
const prefix string = ",\"struct\":"
|
|
||||||
if first {
|
|
||||||
first = false
|
|
||||||
out.RawString(prefix[1:])
|
|
||||||
} else {
|
|
||||||
out.RawString(prefix)
|
|
||||||
}
|
|
||||||
out.String(string(in.Struct))
|
|
||||||
}
|
|
||||||
{
|
|
||||||
const prefix string = ",\"type\":"
|
|
||||||
if first {
|
|
||||||
first = false
|
|
||||||
out.RawString(prefix[1:])
|
|
||||||
} else {
|
|
||||||
out.RawString(prefix)
|
|
||||||
}
|
|
||||||
out.String(string(in.Type))
|
|
||||||
}
|
|
||||||
{
|
|
||||||
const prefix string = ",\"value\":"
|
|
||||||
if first {
|
|
||||||
first = false
|
|
||||||
out.RawString(prefix[1:])
|
|
||||||
} else {
|
|
||||||
out.RawString(prefix)
|
|
||||||
}
|
|
||||||
out.String(string(in.Value))
|
|
||||||
}
|
|
||||||
out.RawByte('}')
|
|
||||||
}
|
|
||||||
|
|
||||||
// MarshalJSON supports json.Marshaler interface
|
|
||||||
func (v Object) MarshalJSON() ([]byte, error) {
|
|
||||||
w := jwriter.Writer{}
|
|
||||||
easyjsonE44bcf2dEncodeGithubComJimehRbheapInspect1(&w, v)
|
|
||||||
return w.Buffer.BuildBytes(), w.Error
|
|
||||||
}
|
|
||||||
|
|
||||||
// MarshalEasyJSON supports easyjson.Marshaler interface
|
|
||||||
func (v Object) MarshalEasyJSON(w *jwriter.Writer) {
|
|
||||||
easyjsonE44bcf2dEncodeGithubComJimehRbheapInspect1(w, v)
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnmarshalJSON supports json.Unmarshaler interface
|
|
||||||
func (v *Object) UnmarshalJSON(data []byte) error {
|
|
||||||
r := jlexer.Lexer{Data: data}
|
|
||||||
easyjsonE44bcf2dDecodeGithubComJimehRbheapInspect1(&r, v)
|
|
||||||
return r.Error()
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnmarshalEasyJSON supports easyjson.Unmarshaler interface
|
|
||||||
func (v *Object) UnmarshalEasyJSON(l *jlexer.Lexer) {
|
|
||||||
easyjsonE44bcf2dDecodeGithubComJimehRbheapInspect1(l, v)
|
|
||||||
}
|
|
||||||
117
inspect/source_inspector.go
Normal file
117
inspect/source_inspector.go
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
package inspect
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"sort"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewSourceInspector creates a new SourceInspector.
|
||||||
|
func NewSourceInspector(filePath string) *SourceInspector {
|
||||||
|
return &SourceInspector{
|
||||||
|
BaseInspector: BaseInspector{
|
||||||
|
FilePath: filePath,
|
||||||
|
Dump: NewDump(filePath),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SourceInspector inspects memory dumps based on the file and line that
|
||||||
|
// allocated the memory.
|
||||||
|
type SourceInspector struct {
|
||||||
|
BaseInspector
|
||||||
|
SortBy string
|
||||||
|
Limit int
|
||||||
|
FileMap map[string]*File
|
||||||
|
}
|
||||||
|
|
||||||
|
// ByFile writes file and line details to given io.Writer.
|
||||||
|
func (s *SourceInspector) ByFile(w io.Writer) {
|
||||||
|
fileMap := map[string]*File{}
|
||||||
|
files := []*File{}
|
||||||
|
|
||||||
|
for _, obj := range s.Dump.Objects {
|
||||||
|
if _, ok := fileMap[obj.File]; !ok {
|
||||||
|
file := NewFile(obj.File)
|
||||||
|
files = append(files, file)
|
||||||
|
fileMap[obj.File] = file
|
||||||
|
}
|
||||||
|
fileMap[obj.File].Add(obj)
|
||||||
|
}
|
||||||
|
|
||||||
|
switch s.SortBy {
|
||||||
|
case "memsize":
|
||||||
|
sort.Slice(files, func(i, j int) bool {
|
||||||
|
return files[i].MemSize > files[j].MemSize
|
||||||
|
})
|
||||||
|
case "bytesize":
|
||||||
|
sort.Slice(files, func(i, j int) bool {
|
||||||
|
return files[i].ByteSize > files[j].ByteSize
|
||||||
|
})
|
||||||
|
default:
|
||||||
|
sort.Slice(files, func(i, j int) bool {
|
||||||
|
return files[i].ObjectCount > files[j].ObjectCount
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, file := range files {
|
||||||
|
fmt.Fprintf(w,
|
||||||
|
"%s (objects: %d, bytesize: %d, memsize: %d)\n",
|
||||||
|
file.FilePath,
|
||||||
|
file.ObjectCount,
|
||||||
|
file.ByteSize,
|
||||||
|
file.MemSize,
|
||||||
|
)
|
||||||
|
|
||||||
|
i++
|
||||||
|
if s.Limit != 0 && i >= s.Limit {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ByLine writes file and line details to given io.Writer.
|
||||||
|
func (s *SourceInspector) ByLine(w io.Writer) {
|
||||||
|
lineMap := map[string]*Line{}
|
||||||
|
lines := []*Line{}
|
||||||
|
|
||||||
|
for _, obj := range s.Dump.Objects {
|
||||||
|
if _, ok := lineMap[obj.File]; !ok {
|
||||||
|
line := NewLine(obj.File, obj.Line)
|
||||||
|
lines = append(lines, line)
|
||||||
|
lineMap[obj.File] = line
|
||||||
|
}
|
||||||
|
lineMap[obj.File].Add(obj)
|
||||||
|
}
|
||||||
|
|
||||||
|
switch s.SortBy {
|
||||||
|
case "memsize":
|
||||||
|
sort.Slice(lines, func(i, j int) bool {
|
||||||
|
return lines[i].MemSize > lines[j].MemSize
|
||||||
|
})
|
||||||
|
case "bytesize":
|
||||||
|
sort.Slice(lines, func(i, j int) bool {
|
||||||
|
return lines[i].ByteSize > lines[j].ByteSize
|
||||||
|
})
|
||||||
|
default:
|
||||||
|
sort.Slice(lines, func(i, j int) bool {
|
||||||
|
return lines[i].ObjectCount > lines[j].ObjectCount
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, file := range lines {
|
||||||
|
fmt.Fprintf(w,
|
||||||
|
"%s:%d (objects: %d, bytesize: %d, memsize: %d)\n",
|
||||||
|
file.FilePath,
|
||||||
|
file.LineNum,
|
||||||
|
file.ObjectCount,
|
||||||
|
file.ByteSize,
|
||||||
|
file.MemSize,
|
||||||
|
)
|
||||||
|
|
||||||
|
i++
|
||||||
|
if s.Limit != 0 && i >= s.Limit {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user