-
Notifications
You must be signed in to change notification settings - Fork 898
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
compiler: add -size flag to replace size utility
The size flag has two modes: -size=short: prints data basically equivalent to the `size` program. -size=full: tries to determine sizes per package (not entirely accurate).
- Loading branch information
Showing
3 changed files
with
189 additions
and
14 deletions.
There are no files selected for viewing
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
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
Original file line number | Original file line | Diff line number | Diff line change |
---|---|---|---|
@@ -0,0 +1,160 @@ | |||
package main | |||
|
|||
import ( | |||
"debug/elf" | |||
"sort" | |||
"strings" | |||
) | |||
|
|||
// Statistics about code size in a program. | |||
type ProgramSize struct { | |||
Packages map[string]*PackageSize | |||
Sum *PackageSize | |||
Code uint64 | |||
Data uint64 | |||
BSS uint64 | |||
} | |||
|
|||
// Return the list of package names (ProgramSize.Packages) sorted | |||
// alphabetically. | |||
func (ps *ProgramSize) SortedPackageNames() []string { | |||
names := make([]string, 0, len(ps.Packages)) | |||
for name := range ps.Packages { | |||
names = append(names, name) | |||
} | |||
sort.Strings(names) | |||
return names | |||
} | |||
|
|||
// The size of a package, calculated from the linked object file. | |||
type PackageSize struct { | |||
Code uint64 | |||
ROData uint64 | |||
Data uint64 | |||
BSS uint64 | |||
} | |||
|
|||
// Flash usage in regular microcontrollers. | |||
func (ps *PackageSize) Flash() uint64 { | |||
return ps.Code + ps.ROData + ps.Data | |||
} | |||
|
|||
// Static RAM usage in regular microcontrollers. | |||
func (ps *PackageSize) RAM() uint64 { | |||
return ps.Data + ps.BSS | |||
} | |||
|
|||
type symbolList []elf.Symbol | |||
|
|||
func (l symbolList) Len() int { | |||
return len(l) | |||
} | |||
|
|||
func (l symbolList) Less(i, j int) bool { | |||
bind_i := elf.ST_BIND(l[i].Info) | |||
bind_j := elf.ST_BIND(l[j].Info) | |||
if l[i].Value == l[j].Value && bind_i != elf.STB_WEAK && bind_j == elf.STB_WEAK { | |||
// sort weak symbols after non-weak symbols | |||
return true | |||
} | |||
return l[i].Value < l[j].Value | |||
} | |||
|
|||
func (l symbolList) Swap(i, j int) { | |||
l[i], l[j] = l[j], l[i] | |||
} | |||
|
|||
// Calculate program/data size breakdown of each package for a given ELF file. | |||
func Sizes(path string) (*ProgramSize, error) { | |||
file, err := elf.Open(path) | |||
if err != nil { | |||
return nil, err | |||
} | |||
defer file.Close() | |||
|
|||
var sumCode uint64 | |||
var sumData uint64 | |||
var sumBSS uint64 | |||
for _, section := range file.Sections { | |||
if section.Flags&elf.SHF_ALLOC == 0 { | |||
continue | |||
} | |||
if section.Type != elf.SHT_PROGBITS && section.Type != elf.SHT_NOBITS { | |||
continue | |||
} | |||
if section.Type == elf.SHT_NOBITS { | |||
sumBSS += section.Size | |||
} else if section.Flags&elf.SHF_EXECINSTR != 0 { | |||
sumCode += section.Size | |||
} else if section.Flags&elf.SHF_WRITE != 0 { | |||
sumData += section.Size | |||
} | |||
} | |||
|
|||
allSymbols, err := file.Symbols() | |||
if err != nil { | |||
return nil, err | |||
} | |||
symbols := make([]elf.Symbol, 0, len(allSymbols)) | |||
for _, symbol := range allSymbols { | |||
symType := elf.ST_TYPE(symbol.Info) | |||
if symbol.Size == 0 { | |||
continue | |||
} | |||
if symType != elf.STT_FUNC && symType != elf.STT_OBJECT && symType != elf.STT_NOTYPE { | |||
continue | |||
} | |||
if symbol.Section >= elf.SectionIndex(len(file.Sections)) { | |||
continue | |||
} | |||
section := file.Sections[symbol.Section] | |||
if section.Flags&elf.SHF_ALLOC == 0 { | |||
continue | |||
} | |||
symbols = append(symbols, symbol) | |||
} | |||
sort.Sort(symbolList(symbols)) | |||
|
|||
sizes := map[string]*PackageSize{} | |||
var lastSymbolValue uint64 | |||
for _, symbol := range symbols { | |||
symType := elf.ST_TYPE(symbol.Info) | |||
//bind := elf.ST_BIND(symbol.Info) | |||
section := file.Sections[symbol.Section] | |||
pkgName := "(bootstrap)" | |||
symName := strings.TrimLeft(symbol.Name, "(*") | |||
dot := strings.IndexByte(symName, '.') | |||
if dot > 0 { | |||
pkgName = symName[:dot] | |||
} | |||
pkgSize := sizes[pkgName] | |||
if pkgSize == nil { | |||
pkgSize = &PackageSize{} | |||
sizes[pkgName] = pkgSize | |||
} | |||
if lastSymbolValue != symbol.Value || lastSymbolValue == 0 { | |||
if symType == elf.STT_FUNC { | |||
pkgSize.Code += symbol.Size | |||
} else if section.Flags&elf.SHF_WRITE != 0 { | |||
if section.Type == elf.SHT_NOBITS { | |||
pkgSize.BSS += symbol.Size | |||
} else { | |||
pkgSize.Data += symbol.Size | |||
} | |||
} else { | |||
pkgSize.ROData += symbol.Size | |||
} | |||
} | |||
lastSymbolValue = symbol.Value | |||
} | |||
|
|||
sum := &PackageSize{} | |||
for _, pkg := range sizes { | |||
sum.Code += pkg.Code | |||
sum.ROData += pkg.ROData | |||
sum.Data += pkg.Data | |||
sum.BSS += pkg.BSS | |||
} | |||
|
|||
return &ProgramSize{Packages: sizes, Code: sumCode, Data: sumData, BSS: sumBSS, Sum: sum}, nil | |||
} |
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