mirror of https://github.com/tinygo-org/tinygo.git
wasmstm32webassemblymicrocontrollerarmavrspiwasiadafruitarduinocircuitplayground-expressgpioi2cllvmmicrobitnrf51nrf52nrf52840samd21tinygo
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
160 lines
3.7 KiB
160 lines
3.7 KiB
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
|
|
}
|
|
|