From 3797871393d551f31966eb38b4f8ae331a42a45a Mon Sep 17 00:00:00 2001 From: Zxilly Date: Sat, 8 Jun 2024 14:19:18 +0800 Subject: [PATCH] refactor: move known info files --- internal/analyze.go | 37 ++-- internal/entity/knownaddr.go | 18 ++ internal/entity/meta.go | 3 + internal/entity/type.go | 1 + internal/knowninfo.go | 229 --------------------- internal/knowninfo/collect.go | 89 ++++++++ internal/{ => knowninfo}/dependencies.go | 76 +++++-- internal/{ => knowninfo}/disasm.go | 4 +- internal/{ => knowninfo}/disasm_js_wasm.go | 6 +- internal/knowninfo/dwarf.go | 52 +++++ internal/knowninfo/knowninfo.go | 52 +++++ internal/knowninfo/section.go | 19 ++ internal/{ => knowninfo}/symbol.go | 37 +++- internal/{ => section}/section.go | 2 +- internal/{ => section}/section_test.go | 2 +- internal/wrapper/elf.go | 5 + internal/wrapper/macho.go | 5 + internal/wrapper/pe.go | 5 + internal/wrapper/wrapper.go | 2 + 19 files changed, 380 insertions(+), 264 deletions(-) delete mode 100644 internal/knowninfo.go create mode 100644 internal/knowninfo/collect.go rename internal/{ => knowninfo}/dependencies.go (60%) rename internal/{ => knowninfo}/disasm.go (96%) rename internal/{ => knowninfo}/disasm_js_wasm.go (73%) create mode 100644 internal/knowninfo/dwarf.go create mode 100644 internal/knowninfo/knowninfo.go create mode 100644 internal/knowninfo/section.go rename internal/{ => knowninfo}/symbol.go (60%) rename internal/{ => section}/section.go (97%) rename internal/{ => section}/section_test.go (99%) diff --git a/internal/analyze.go b/internal/analyze.go index cc85521f30..ac4ea81414 100644 --- a/internal/analyze.go +++ b/internal/analyze.go @@ -2,6 +2,7 @@ package internal import ( "errors" + "github.com/Zxilly/go-size-analyzer/internal/knowninfo" "io" "log/slog" "path" @@ -30,15 +31,15 @@ func Analyze(name string, reader io.ReaderAt, size uint64, options Options) (*re slog.Info("Parsing binary done") slog.Info("Finding build info...") - k := &KnownInfo{ + k := &knowninfo.KnownInfo{ Size: size, BuildInfo: file.BuildInfo, - gore: file, - wrapper: wrapper.NewWrapper(file.GetParsedFile()), + Gore: file, + Wrapper: wrapper.NewWrapper(file.GetParsedFile()), } k.KnownAddr = entity.NewKnownAddr() - k.UpdateVersionFlag() + k.VersionFlag = k.UpdateVersionFlag() slog.Info("Build info found") @@ -52,21 +53,25 @@ func Analyze(name string, reader io.ReaderAt, size uint64, options Options) (*re return nil, err } - if !options.SkipSymbol { - err = k.AnalyzeSymbol() - if err != nil { - if !errors.Is(err, wrapper.ErrNoSymbolTable) { - return nil, err - + ok := k.TryLoadDwarf() + if !ok { + // fallback to symbol and disasm + if !options.SkipSymbol { + err = k.AnalyzeSymbol() + if err != nil { + if !errors.Is(err, wrapper.ErrNoSymbolTable) { + return nil, err + + } + slog.Warn("Warning: no symbol table found, this can lead to inaccurate results") } - slog.Warn("Warning: no symbol table found, this can lead to inaccurate results") } - } - if !options.SkipDisasm { - err = k.Disasm() - if err != nil { - return nil, err + if !options.SkipDisasm { + err = k.Disasm() + if err != nil { + return nil, err + } } } diff --git a/internal/entity/knownaddr.go b/internal/entity/knownaddr.go index 43c97a26bc..ce7182142b 100644 --- a/internal/entity/knownaddr.go +++ b/internal/entity/knownaddr.go @@ -10,6 +10,8 @@ type KnownAddr struct { Symbol AddrSpace SymbolCoverage AddrCoverage + + Dwarf AddrSpace } func NewKnownAddr() *KnownAddr { @@ -104,3 +106,19 @@ func (f *KnownAddr) InsertDisasm(entry uint64, size uint64, fn *Function, meta D fn.disasm.Insert(&cur) } + +func (f *KnownAddr) InsertDwarf(entry uint64, size uint64, typ AddrType, pkg *Package, meta DwarfMeta) { + cur := Addr{ + AddrPos: &AddrPos{ + Addr: entry, + Size: size, + Type: typ, + }, + Pkg: pkg, + Function: nil, + SourceType: AddrSourceDwarf, + Meta: meta, + } + + f.Dwarf.Insert(&cur) +} diff --git a/internal/entity/meta.go b/internal/entity/meta.go index f96c4d5658..61f3091762 100644 --- a/internal/entity/meta.go +++ b/internal/entity/meta.go @@ -16,3 +16,6 @@ type SymbolMeta struct { type DisasmMeta struct { Value string } + +type DwarfMeta struct { +} diff --git a/internal/entity/type.go b/internal/entity/type.go index 5d119be2b3..4e8b3a2bcb 100644 --- a/internal/entity/type.go +++ b/internal/entity/type.go @@ -14,4 +14,5 @@ const ( AddrSourceGoPclntab AddrSourceType = "pclntab" AddrSourceSymbol AddrSourceType = "symbol" AddrSourceDisasm AddrSourceType = "disasm" + AddrSourceDwarf AddrSourceType = "dwarf" ) diff --git a/internal/knowninfo.go b/internal/knowninfo.go deleted file mode 100644 index b987b10e0d..0000000000 --- a/internal/knowninfo.go +++ /dev/null @@ -1,229 +0,0 @@ -package internal - -import ( - "fmt" - "log/slog" - "math" - "runtime/debug" - - "github.com/ZxillyFork/gore" - "github.com/ZxillyFork/gosym" - - "github.com/Zxilly/go-size-analyzer/internal/entity" - "github.com/Zxilly/go-size-analyzer/internal/utils" - "github.com/Zxilly/go-size-analyzer/internal/wrapper" -) - -type KnownInfo struct { - Size uint64 - BuildInfo *gore.BuildInfo - Sects *SectionMap - Deps *Dependencies - KnownAddr *entity.KnownAddr - - Coverage entity.AddrCoverage - - gore *gore.GoFile - wrapper wrapper.RawFileWrapper - - VersionFlag struct { - Leq118 bool - Meq120 bool - } -} - -func (k *KnownInfo) LoadSectionMap() error { - slog.Info("Loading sections...") - - sections := k.wrapper.LoadSections() - - slog.Info("Loading sections done") - - k.Sects = &SectionMap{ - Sections: sections, - } - return k.Sects.AssertSize(k.Size) -} - -func (k *KnownInfo) AnalyzeSymbol() error { - slog.Info("Analyzing symbols...") - - err := k.wrapper.LoadSymbols(k.MarkSymbol) - if err != nil { - return err - } - - k.KnownAddr.BuildSymbolCoverage() - - slog.Info("Analyzing symbols done") - - return nil -} - -func (k *KnownInfo) UpdateVersionFlag() { - ver, err := k.gore.GetCompilerVersion() - if err != nil { - // if we can't get build info, we assume it's go1.20 plus - k.VersionFlag.Meq120 = true - } else { - k.VersionFlag.Leq118 = gore.GoVersionCompare(ver.Name, "go1.18.10") <= 0 - k.VersionFlag.Meq120 = gore.GoVersionCompare(ver.Name, "go1.20rc1") >= 0 - } -} - -// ExtractPackageFromSymbol copied from debug/gosym/symtab.go -func (k *KnownInfo) ExtractPackageFromSymbol(s string) string { - var ver gosym.Version - if k.VersionFlag.Meq120 { - ver = gosym.Ver120 // ver120 - } else if k.VersionFlag.Leq118 { - ver = gosym.Ver118 // ver118 - } - - sym := &gosym.Sym{ - Name: s, - GoVersion: ver, - } - - packageName := sym.PackageName() - - return utils.UglyGuess(packageName) -} - -func (k *KnownInfo) LoadPackages() error { - slog.Info("Loading packages...") - - pkgs := NewDependencies(k) - k.Deps = pkgs - - pclntab, err := k.gore.PCLNTab() - if err != nil { - return err - } - - self, err := k.gore.GetPackages() - if err != nil { - return err - } - for _, p := range self { - pkgs.Add(p, entity.PackageTypeMain, pclntab) - } - - grStd, _ := k.gore.GetSTDLib() - for _, p := range grStd { - pkgs.Add(p, entity.PackageTypeStd, pclntab) - } - - grVendor, _ := k.gore.GetVendors() - for _, p := range grVendor { - pkgs.Add(p, entity.PackageTypeVendor, pclntab) - } - - grGenerated, _ := k.gore.GetGeneratedPackages() - for _, p := range grGenerated { - pkgs.Add(p, entity.PackageTypeGenerated, pclntab) - } - - grUnknown, _ := k.gore.GetUnknown() - for _, p := range grUnknown { - pkgs.Add(p, entity.PackageTypeUnknown, pclntab) - } - - if err = k.RequireModInfo(); err == nil { - pkgs.AddModules(k.BuildInfo.ModInfo.Deps, entity.PackageTypeVendor) - pkgs.AddModules([]*debug.Module{&k.BuildInfo.ModInfo.Main}, entity.PackageTypeVendor) - } - - pkgs.FinishLoad() - - slog.Info("Loading packages done") - - return nil -} - -func (k *KnownInfo) RequireModInfo() error { - if k.BuildInfo == nil { - return fmt.Errorf("no build info") - } - return nil -} - -func (k *KnownInfo) CollectCoverage() error { - // load coverage for pclntab and symbol - pclntabCov := k.KnownAddr.Pclntab.ToDirtyCoverage() - - // merge all - covs := make([]entity.AddrCoverage, 0) - - // collect packages coverage - _ = k.Deps.trie.Walk(func(_ string, p *entity.Package) error { - covs = append(covs, p.GetPackageCoverage()) - return nil - }) - - covs = append(covs, pclntabCov, k.KnownAddr.SymbolCoverage) - - var err error - k.Coverage, err = entity.MergeAndCleanCoverage(covs) - return err -} - -func (k *KnownInfo) CalculateSectionSize() error { - t := make(map[*entity.Section]uint64) - // minus coverage part - for _, cp := range k.Coverage { - section := k.Sects.FindSection(cp.Pos.Addr, cp.Pos.Size) - if section == nil { - slog.Debug(fmt.Sprintf("section not found for coverage part %s", cp)) - continue - } - t[section] += cp.Pos.Size - } - - pclntabSize := uint64(0) - _ = k.Deps.trie.Walk(func(_ string, p *entity.Package) error { - for _, fn := range p.GetFunctions() { - pclntabSize += fn.PclnSize.Size() - } - return nil - }) - - // minus pclntab size - possibleNames := k.wrapper.PclntabSections() - for name, section := range k.Sects.Sections { - for _, possibleName := range possibleNames { - if possibleName == name { - t[section] += pclntabSize - goto foundPclntab - } - } - } - return fmt.Errorf("pclntab section not found when calculate known size") -foundPclntab: - - // linear map virtual size to file size - for section, size := range t { - mapper := 1.0 - if section.Size != section.FileSize { - // need to map to file size - mapper = float64(section.FileSize) / float64(section.Size) - } - section.KnownSize = uint64(math.Floor(float64(size) * mapper)) - - if section.KnownSize > section.FileSize { - // fixme: pclntab size calculation is not accurate - slog.Warn(fmt.Sprintf("section %s known size %d > file size %d, this is a known issue", section.Name, section.KnownSize, section.FileSize)) - section.KnownSize = section.FileSize - } - } - return nil -} - -// CalculatePackageSize calculate the size of each package -// Happens after disassembly -func (k *KnownInfo) CalculatePackageSize() { - _ = k.Deps.trie.Walk(func(_ string, p *entity.Package) error { - p.AssignPackageSize() - return nil - }) -} diff --git a/internal/knowninfo/collect.go b/internal/knowninfo/collect.go new file mode 100644 index 0000000000..124656f7ab --- /dev/null +++ b/internal/knowninfo/collect.go @@ -0,0 +1,89 @@ +package knowninfo + +import ( + "fmt" + "log/slog" + "math" + + "github.com/Zxilly/go-size-analyzer/internal/entity" +) + +func (k *KnownInfo) CollectCoverage() error { + // load coverage for pclntab and symbol + pclntabCov := k.KnownAddr.Pclntab.ToDirtyCoverage() + + // merge all + covs := make([]entity.AddrCoverage, 0) + + // collect packages coverage + _ = k.Deps.Trie.Walk(func(_ string, p *entity.Package) error { + covs = append(covs, p.GetPackageCoverage()) + return nil + }) + + covs = append(covs, pclntabCov, k.KnownAddr.SymbolCoverage) + + var err error + k.Coverage, err = entity.MergeAndCleanCoverage(covs) + return err +} + +func (k *KnownInfo) CalculateSectionSize() error { + t := make(map[*entity.Section]uint64) + // minus coverage part + for _, cp := range k.Coverage { + s := k.Sects.FindSection(cp.Pos.Addr, cp.Pos.Size) + if s == nil { + slog.Debug(fmt.Sprintf("section not found for coverage part %s", cp)) + continue + } + t[s] += cp.Pos.Size + } + + pclntabSize := uint64(0) + _ = k.Deps.Trie.Walk(func(_ string, p *entity.Package) error { + for _, fn := range p.GetFunctions() { + pclntabSize += fn.PclnSize.Size() + } + return nil + }) + + // minus pclntab size + possibleNames := k.Wrapper.PclntabSections() + for name, s := range k.Sects.Sections { + for _, possibleName := range possibleNames { + if possibleName == name { + t[s] += pclntabSize + goto foundPclntab + } + } + } + return fmt.Errorf("pclntab section not found when calculate known size") +foundPclntab: + + // linear map virtual size to file size + for s, size := range t { + mapper := 1.0 + if s.Size != s.FileSize { + // need to map to file size + mapper = float64(s.FileSize) / float64(s.Size) + } + s.KnownSize = uint64(math.Floor(float64(size) * mapper)) + + if s.KnownSize > s.FileSize { + // fixme: pclntab size calculation is not accurate + slog.Warn(fmt.Sprintf("section %s known size %d > file size %d, this is a known issue", s.Name, s.KnownSize, s.FileSize)) + s.KnownSize = s.FileSize + } + } + return nil +} + +// CalculatePackageSize calculate the size of each package +// Happens after disassembly +func (k *KnownInfo) CalculatePackageSize() { + _ = k.Deps.Trie.Walk(func(_ string, p *entity.Package) error { + p.AssignPackageSize() + return nil + }) +} diff --git a/internal/dependencies.go b/internal/knowninfo/dependencies.go similarity index 60% rename from internal/dependencies.go rename to internal/knowninfo/dependencies.go index 845c6ddbb9..05fc4eb5aa 100644 --- a/internal/dependencies.go +++ b/internal/knowninfo/dependencies.go @@ -1,6 +1,7 @@ -package internal +package knowninfo import ( + "log/slog" "runtime/debug" "github.com/ZxillyFork/gore" @@ -16,19 +17,19 @@ type Dependencies struct { k *KnownInfo TopPkgs entity.PackageMap - trie *trie.PathTrie[*entity.Package] + Trie *trie.PathTrie[*entity.Package] } func NewDependencies(k *KnownInfo) *Dependencies { return &Dependencies{ TopPkgs: make(entity.PackageMap), k: k, - trie: trie.NewPathTrie[*entity.Package](), + Trie: trie.NewPathTrie[*entity.Package](), } } func (m *Dependencies) GetPackage(name string) (*entity.Package, bool) { - p := m.trie.Get(name) + p := m.Trie.Get(name) if p == nil { return nil, false } @@ -37,7 +38,7 @@ func (m *Dependencies) GetPackage(name string) (*entity.Package, bool) { func (m *Dependencies) GetFunctions() []*entity.Function { funcs := make([]*entity.Function, 0) - _ = m.trie.Walk(func(_ string, p *entity.Package) error { + _ = m.Trie.Walk(func(_ string, p *entity.Package) error { funcs = append(funcs, p.GetFunctions()...) return nil }) @@ -46,7 +47,7 @@ func (m *Dependencies) GetFunctions() []*entity.Function { func (m *Dependencies) AddModules(mods []*debug.Module, typ entity.PackageType) { for _, mod := range mods { - old := m.trie.Get(mod.Path) + old := m.Trie.Get(mod.Path) if old != nil { old.DebugMod = mod continue @@ -55,7 +56,7 @@ func (m *Dependencies) AddModules(mods []*debug.Module, typ entity.PackageType) p.Name = utils.Deduplicate(mod.Path) p.Type = typ p.DebugMod = mod - m.trie.Put(mod.Path, p) + m.Trie.Put(mod.Path, p) } } @@ -66,11 +67,11 @@ func (m *Dependencies) FinishLoad() { } // load generated packages, they don't have a path - if m.trie.Value != nil { - m.TopPkgs[""] = *m.trie.Value + if m.Trie.Value != nil { + m.TopPkgs[""] = *m.Trie.Value } - pending := []pair{{m.TopPkgs, m.trie}} + pending := []pair{{m.TopPkgs, m.Trie}} load := func(packageMap entity.PackageMap, p *trie.PathTrie[*entity.Package]) { for part, nxt := range p.RecursiveDirectChildren() { @@ -106,10 +107,61 @@ func (m *Dependencies) Add(gp *gore.Package, typ entity.PackageType, pclntab *go } // we need merge since the gore relies on the broken std PackageName() function - old := m.trie.Get(name) + old := m.Trie.Get(name) if old != nil { // merge the old one p.Merge(old) } - m.trie.Put(name, p) + m.Trie.Put(name, p) +} + +func (k *KnownInfo) LoadPackages() error { + slog.Info("Loading packages...") + + pkgs := NewDependencies(k) + k.Deps = pkgs + + pclntab, err := k.Gore.PCLNTab() + if err != nil { + return err + } + + self, err := k.Gore.GetPackages() + if err != nil { + return err + } + for _, p := range self { + pkgs.Add(p, entity.PackageTypeMain, pclntab) + } + + grStd, _ := k.Gore.GetSTDLib() + for _, p := range grStd { + pkgs.Add(p, entity.PackageTypeStd, pclntab) + } + + grVendor, _ := k.Gore.GetVendors() + for _, p := range grVendor { + pkgs.Add(p, entity.PackageTypeVendor, pclntab) + } + + grGenerated, _ := k.Gore.GetGeneratedPackages() + for _, p := range grGenerated { + pkgs.Add(p, entity.PackageTypeGenerated, pclntab) + } + + grUnknown, _ := k.Gore.GetUnknown() + for _, p := range grUnknown { + pkgs.Add(p, entity.PackageTypeUnknown, pclntab) + } + + if err = k.RequireModInfo(); err == nil { + pkgs.AddModules(k.BuildInfo.ModInfo.Deps, entity.PackageTypeVendor) + pkgs.AddModules([]*debug.Module{&k.BuildInfo.ModInfo.Main}, entity.PackageTypeVendor) + } + + pkgs.FinishLoad() + + slog.Info("Loading packages done") + + return nil } diff --git a/internal/disasm.go b/internal/knowninfo/disasm.go similarity index 96% rename from internal/disasm.go rename to internal/knowninfo/disasm.go index 4eeb1aa3a1..9fafc2fb73 100644 --- a/internal/disasm.go +++ b/internal/knowninfo/disasm.go @@ -1,6 +1,6 @@ //go:build !wasm -package internal +package knowninfo import ( "context" @@ -24,7 +24,7 @@ func (k *KnownInfo) Disasm() error { fns := k.Deps.GetFunctions() - e, err := disasm.NewExtractor(k.wrapper, k.Size) + e, err := disasm.NewExtractor(k.Wrapper, k.Size) if err != nil { if errors.Is(err, disasm.ErrArchNotSupported) { slog.Warn("Warning: disassembler not supported for this architecture") diff --git a/internal/disasm_js_wasm.go b/internal/knowninfo/disasm_js_wasm.go similarity index 73% rename from internal/disasm_js_wasm.go rename to internal/knowninfo/disasm_js_wasm.go index 2d1efad6d2..cb50d7c5f8 100644 --- a/internal/disasm_js_wasm.go +++ b/internal/knowninfo/disasm_js_wasm.go @@ -1,8 +1,10 @@ //go:build wasm -package internal +package knowninfo -import "log/slog" +import ( + "log/slog" +) func (k *KnownInfo) Disasm() error { slog.Info("disassembler disabled for wasm") diff --git a/internal/knowninfo/dwarf.go b/internal/knowninfo/dwarf.go new file mode 100644 index 0000000000..2f916cd8a5 --- /dev/null +++ b/internal/knowninfo/dwarf.go @@ -0,0 +1,52 @@ +package knowninfo + +import ( + "debug/dwarf" + "fmt" + "log/slog" + "os" + "strings" +) + +func (k *KnownInfo) TryLoadDwarf() bool { + dwarf, err := k.Wrapper.DWARF() + if err != nil { + slog.Warn(fmt.Sprintf("Failed to load DWARF: %v", err)) + return false + } + + out, err := os.OpenFile("dwarf.log", os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0644) + if err != nil { + panic(err) + } + defer out.Close() + + r := dwarf.Reader() + for { + entry, err := r.Next() + if entry == nil { + break + } + if err != nil { + slog.Warn(fmt.Sprintf("Failed to read DWARF entry: %#v", err)) + break + } + + fmt.Fprintf(out, "DWARF entry: %v\n", entryPrettyPrinter(entry)) + } + + return true +} + +func entryPrettyPrinter(entry *dwarf.Entry) string { + s := new(strings.Builder) + + fmt.Fprintf(s, "Offset: %v\n", entry.Offset) + fmt.Fprintf(s, "Tag: %v\n", entry.Tag.String()) + fmt.Fprintf(s, "Children: %v\n", entry.Children) + for _, field := range entry.Field { + fmt.Fprintf(s, "Field: %#v\n", field) + } + + return s.String() +} diff --git a/internal/knowninfo/knowninfo.go b/internal/knowninfo/knowninfo.go new file mode 100644 index 0000000000..638c30bcfd --- /dev/null +++ b/internal/knowninfo/knowninfo.go @@ -0,0 +1,52 @@ +package knowninfo + +import ( + "fmt" + "github.com/Zxilly/go-size-analyzer/internal/entity" + "github.com/Zxilly/go-size-analyzer/internal/section" + "github.com/Zxilly/go-size-analyzer/internal/wrapper" + "github.com/ZxillyFork/gore" +) + +type VersionFlag struct { + Leq118 bool + Meq120 bool +} + +type KnownInfo struct { + Size uint64 + BuildInfo *gore.BuildInfo + Sects *section.SectionMap + Deps *Dependencies + KnownAddr *entity.KnownAddr + + Coverage entity.AddrCoverage + + Gore *gore.GoFile + Wrapper wrapper.RawFileWrapper + + VersionFlag VersionFlag +} + +func (k *KnownInfo) UpdateVersionFlag() VersionFlag { + ver, err := k.Gore.GetCompilerVersion() + if err != nil { + // if we can't get build info, we assume it's go1.20 plus + return VersionFlag{ + Leq118: false, + Meq120: true, + } + } + + return VersionFlag{ + Leq118: gore.GoVersionCompare(ver.Name, "go1.18.10") <= 0, + Meq120: gore.GoVersionCompare(ver.Name, "go1.20rc1") >= 0, + } +} + +func (k *KnownInfo) RequireModInfo() error { + if k.BuildInfo == nil { + return fmt.Errorf("no build info") + } + return nil +} diff --git a/internal/knowninfo/section.go b/internal/knowninfo/section.go new file mode 100644 index 0000000000..02d553b1a2 --- /dev/null +++ b/internal/knowninfo/section.go @@ -0,0 +1,19 @@ +package knowninfo + +import ( + "github.com/Zxilly/go-size-analyzer/internal/section" + "log/slog" +) + +func (k *KnownInfo) LoadSectionMap() error { + slog.Info("Loading sections...") + + sections := k.Wrapper.LoadSections() + + slog.Info("Loading sections done") + + k.Sects = §ion.SectionMap{ + Sections: sections, + } + return k.Sects.AssertSize(k.Size) +} diff --git a/internal/symbol.go b/internal/knowninfo/symbol.go similarity index 60% rename from internal/symbol.go rename to internal/knowninfo/symbol.go index 3834477d45..33dd6e2f1f 100644 --- a/internal/symbol.go +++ b/internal/knowninfo/symbol.go @@ -1,6 +1,7 @@ -package internal +package knowninfo import ( + "github.com/ZxillyFork/gosym" "log/slog" "strings" @@ -8,6 +9,25 @@ import ( "github.com/Zxilly/go-size-analyzer/internal/utils" ) +// ExtractPackageFromSymbol copied from debug/gosym/symtab.go +func (k *KnownInfo) ExtractPackageFromSymbol(s string) string { + var ver gosym.Version + if k.VersionFlag.Meq120 { + ver = gosym.Ver120 // ver120 + } else if k.VersionFlag.Leq118 { + ver = gosym.Ver118 // ver118 + } + + sym := &gosym.Sym{ + Name: s, + GoVersion: ver, + } + + packageName := sym.PackageName() + + return utils.UglyGuess(packageName) +} + func (k *KnownInfo) MarkSymbol(name string, addr, size uint64, typ entity.AddrType) error { if typ != entity.AddrTypeData { // todo: support text symbols, cross check with pclntab @@ -42,3 +62,18 @@ func (k *KnownInfo) MarkSymbol(name string, addr, size uint64, typ entity.AddrTy return nil } + +func (k *KnownInfo) AnalyzeSymbol() error { + slog.Info("Analyzing symbols...") + + err := k.Wrapper.LoadSymbols(k.MarkSymbol) + if err != nil { + return err + } + + k.KnownAddr.BuildSymbolCoverage() + + slog.Info("Analyzing symbols done") + + return nil +} diff --git a/internal/section.go b/internal/section/section.go similarity index 97% rename from internal/section.go rename to internal/section/section.go index bb322a746d..c37968af4d 100644 --- a/internal/section.go +++ b/internal/section/section.go @@ -1,4 +1,4 @@ -package internal +package section import ( "fmt" diff --git a/internal/section_test.go b/internal/section/section_test.go similarity index 99% rename from internal/section_test.go rename to internal/section/section_test.go index 604ac492fa..523936ba74 100644 --- a/internal/section_test.go +++ b/internal/section/section_test.go @@ -1,4 +1,4 @@ -package internal +package section import ( "reflect" diff --git a/internal/wrapper/elf.go b/internal/wrapper/elf.go index 7f436f759a..539a986030 100644 --- a/internal/wrapper/elf.go +++ b/internal/wrapper/elf.go @@ -1,6 +1,7 @@ package wrapper import ( + "debug/dwarf" "debug/elf" "encoding/binary" "errors" @@ -14,6 +15,10 @@ type ElfWrapper struct { file *elf.File } +func (e *ElfWrapper) DWARF() (*dwarf.Data, error) { + return e.file.DWARF() +} + func (*ElfWrapper) PclntabSections() []string { return []string{".gopclntab", ".data.rel.ro.gopclntab", ".data.rel.ro"} } diff --git a/internal/wrapper/macho.go b/internal/wrapper/macho.go index 76545862cf..8d7d8c033f 100644 --- a/internal/wrapper/macho.go +++ b/internal/wrapper/macho.go @@ -1,6 +1,7 @@ package wrapper import ( + "debug/dwarf" "debug/macho" "fmt" "slices" @@ -13,6 +14,10 @@ type MachoWrapper struct { file *macho.File } +func (m *MachoWrapper) DWARF() (*dwarf.Data, error) { + return m.file.DWARF() +} + func (*MachoWrapper) PclntabSections() []string { return []string{"__gopclntab __TEXT", "__gopclntab __DATA_CONST"} } diff --git a/internal/wrapper/pe.go b/internal/wrapper/pe.go index 8c419e2773..5049bd19c1 100644 --- a/internal/wrapper/pe.go +++ b/internal/wrapper/pe.go @@ -1,6 +1,7 @@ package wrapper import ( + "debug/dwarf" "debug/pe" "fmt" "slices" @@ -14,6 +15,10 @@ type PeWrapper struct { file *pe.File } +func (p *PeWrapper) DWARF() (*dwarf.Data, error) { + return p.file.DWARF() +} + func (*PeWrapper) PclntabSections() []string { return []string{".rdata"} // FIXME: get real position from gore, can be .text } diff --git a/internal/wrapper/wrapper.go b/internal/wrapper/wrapper.go index 49dfec0732..cfd922032e 100644 --- a/internal/wrapper/wrapper.go +++ b/internal/wrapper/wrapper.go @@ -1,6 +1,7 @@ package wrapper import ( + "debug/dwarf" "debug/elf" "debug/macho" "debug/pe" @@ -18,6 +19,7 @@ type RawFileWrapper interface { LoadSymbols(marker func(name string, addr, size uint64, typ entity.AddrType) error) error LoadSections() map[string]*entity.Section PclntabSections() []string + DWARF() (*dwarf.Data, error) } func NewWrapper(file any) RawFileWrapper {