How much space is used by Go packages, C libraries, and other bits to set up the program environment.
+
+
+
Code is the actual program code (machine code instructions).
+
Read-only data are read-only global variables. On most microcontrollers, these are stored in flash and do not take up any RAM.
+
Data are writable global variables with a non-zero initializer. On microcontrollers, they are copied from flash to RAM on reset.
+
BSS are writable global variables that are zero initialized. They do not take up any space in the binary, but do take up RAM. On microcontrollers, this area is zeroed on reset.
+
+
+
The binary size consists of code, read-only data, and data. On microcontrollers, this is exactly the size of the firmware image. On other systems, there is some extra overhead: binary metadata (headers of the ELF/MachO/COFF file), debug information, exception tables, symbol names, etc. Using -no-debug strips most of those.
+
+
Program breakdown
+
+
+
+
+
Package
+
Code
+
Read-only data
+
Data
+
BSS
+
Binary size
+
+
+
+ {{range .sizes}}
+
+
{{.Name}}
+
{{.Size.Code}}
+
{{.Size.ROData}}
+
{{.Size.Data}}
+
{{.Size.BSS}}
+
+ {{.Size.Flash}}
+
+
+ {{end}}
+
+
+
+
Total
+
{{.sizeTotal.code}}
+
{{.sizeTotal.rodata}}
+
{{.sizeTotal.data}}
+
{{.sizeTotal.bss}}
+
{{.sizeTotal.flash}}
+
+
+
+
+
+
+
diff --git a/builder/sizes.go b/builder/sizes.go
index 3f6cc4518..7e6eefb3c 100644
--- a/builder/sizes.go
+++ b/builder/sizes.go
@@ -25,7 +25,7 @@ const sizesDebug = false
// programSize contains size statistics per package of a compiled program.
type programSize struct {
- Packages map[string]packageSize
+ Packages map[string]*packageSize
Code uint64
ROData uint64
Data uint64
@@ -56,10 +56,11 @@ func (ps *programSize) RAM() uint64 {
// packageSize contains the size of a package, calculated from the linked object
// file.
type packageSize struct {
- Code uint64
- ROData uint64
- Data uint64
- BSS uint64
+ Program *programSize
+ Code uint64
+ ROData uint64
+ Data uint64
+ BSS uint64
}
// Flash usage in regular microcontrollers.
@@ -72,6 +73,12 @@ func (ps *packageSize) RAM() uint64 {
return ps.Data + ps.BSS
}
+// Flash usage in regular microcontrollers, as a percentage of the total flash
+// usage of the program.
+func (ps *packageSize) FlashPercent() float64 {
+ return float64(ps.Flash()) / float64(ps.Program.Flash()) * 100
+}
+
// A mapping of a single chunk of code or data to a file path.
type addressLine struct {
Address uint64
@@ -785,49 +792,48 @@ func loadProgramSize(path string, packagePathMap map[string]string) (*programSiz
// Now finally determine the binary/RAM size usage per package by going
// through each allocated section.
- sizes := make(map[string]packageSize)
+ sizes := make(map[string]*packageSize)
+ program := &programSize{
+ Packages: sizes,
+ }
+ getSize := func(path string) *packageSize {
+ if field, ok := sizes[path]; ok {
+ return field
+ }
+ field := &packageSize{Program: program}
+ sizes[path] = field
+ return field
+ }
for _, section := range sections {
switch section.Type {
case memoryCode:
readSection(section, addresses, func(path string, size uint64, isVariable bool) {
- field := sizes[path]
+ field := getSize(path)
if isVariable {
field.ROData += size
} else {
field.Code += size
}
- sizes[path] = field
}, packagePathMap)
case memoryROData:
readSection(section, addresses, func(path string, size uint64, isVariable bool) {
- field := sizes[path]
- field.ROData += size
- sizes[path] = field
+ getSize(path).ROData += size
}, packagePathMap)
case memoryData:
readSection(section, addresses, func(path string, size uint64, isVariable bool) {
- field := sizes[path]
- field.Data += size
- sizes[path] = field
+ getSize(path).Data += size
}, packagePathMap)
case memoryBSS:
readSection(section, addresses, func(path string, size uint64, isVariable bool) {
- field := sizes[path]
- field.BSS += size
- sizes[path] = field
+ getSize(path).BSS += size
}, packagePathMap)
case memoryStack:
// We store the C stack as a pseudo-package.
- sizes["C stack"] = packageSize{
- BSS: section.Size,
- }
+ getSize("C stack").BSS += section.Size
}
}
// ...and summarize the results.
- program := &programSize{
- Packages: sizes,
- }
for _, pkg := range sizes {
program.Code += pkg.Code
program.ROData += pkg.ROData
diff --git a/compileopts/options.go b/compileopts/options.go
index bc462b29b..30e0e4dbe 100644
--- a/compileopts/options.go
+++ b/compileopts/options.go
@@ -12,7 +12,7 @@ var (
validGCOptions = []string{"none", "leaking", "conservative", "custom", "precise"}
validSchedulerOptions = []string{"none", "tasks", "asyncify"}
validSerialOptions = []string{"none", "uart", "usb", "rtt"}
- validPrintSizeOptions = []string{"none", "short", "full"}
+ validPrintSizeOptions = []string{"none", "short", "full", "html"}
validPanicStrategyOptions = []string{"print", "trap"}
validOptOptions = []string{"none", "0", "1", "2", "s", "z"}
)
diff --git a/compileopts/options_test.go b/compileopts/options_test.go
index 23ffec465..ee63c4c46 100644
--- a/compileopts/options_test.go
+++ b/compileopts/options_test.go
@@ -11,7 +11,7 @@ func TestVerifyOptions(t *testing.T) {
expectedGCError := errors.New(`invalid gc option 'incorrect': valid values are none, leaking, conservative, custom, precise`)
expectedSchedulerError := errors.New(`invalid scheduler option 'incorrect': valid values are none, tasks, asyncify`)
- expectedPrintSizeError := errors.New(`invalid size option 'incorrect': valid values are none, short, full`)
+ expectedPrintSizeError := errors.New(`invalid size option 'incorrect': valid values are none, short, full, html`)
expectedPanicStrategyError := errors.New(`invalid panic option 'incorrect': valid values are print, trap`)
testCases := []struct {
diff --git a/main.go b/main.go
index 8ae5ce316..9f0cc631a 100644
--- a/main.go
+++ b/main.go
@@ -1509,7 +1509,7 @@ func main() {
stackSize = uint64(size)
return err
})
- printSize := flag.String("size", "", "print sizes (none, short, full)")
+ printSize := flag.String("size", "", "print sizes (none, short, full, html)")
printStacks := flag.Bool("print-stacks", false, "print stack sizes of goroutines")
printAllocsString := flag.String("print-allocs", "", "regular expression of functions for which heap allocations should be printed")
printCommands := flag.Bool("x", false, "Print commands")