@@ -53,6 +53,20 @@ func (ps *programSize) RAM() uint64 {
5353return ps .Data + ps .BSS
5454}
5555
56+ // Return the package size information for a given package path, creating it if
57+ // it doesn't exist yet.
58+ func (ps * programSize ) getPackage (path string ) * packageSize {
59+ if field , ok := ps .Packages [path ]; ok {
60+ return field
61+ }
62+ field := & packageSize {
63+ Program : ps ,
64+ Sub : map [string ]* packageSize {},
65+ }
66+ ps .Packages [path ] = field
67+ return field
68+ }
69+
5670// packageSize contains the size of a package, calculated from the linked object
5771// file.
5872type packageSize struct {
@@ -61,6 +75,7 @@ type packageSize struct {
6175ROData uint64
6276Data uint64
6377BSS uint64
78+ Sub map [string ]* packageSize
6479}
6580
6681// Flash usage in regular microcontrollers.
@@ -79,6 +94,25 @@ func (ps *packageSize) FlashPercent() float64 {
7994return float64 (ps .Flash ()) / float64 (ps .Program .Flash ()) * 100
8095}
8196
97+ // Add a single size data point to this package.
98+ // This must only be called while calculating package size, not afterwards.
99+ func (ps * packageSize ) addSize (getField func (* packageSize , bool ) * uint64 , filename string , size uint64 , isVariable bool ) {
100+ if size == 0 {
101+ return
102+ }
103+
104+ // Add size for the package.
105+ * getField (ps , isVariable ) += size
106+
107+ // Add size for file inside package.
108+ sub , ok := ps .Sub [filename ]
109+ if ! ok {
110+ sub = & packageSize {Program : ps .Program }
111+ ps .Sub [filename ] = sub
112+ }
113+ * getField (sub , isVariable ) += size
114+ }
115+
82116// A mapping of a single chunk of code or data to a file path.
83117type addressLine struct {
84118Address uint64
@@ -796,40 +830,32 @@ func loadProgramSize(path string, packagePathMap map[string]string) (*programSiz
796830program := & programSize {
797831Packages : sizes ,
798832}
799- getSize := func (path string ) * packageSize {
800- if field , ok := sizes [path ]; ok {
801- return field
802- }
803- field := & packageSize {Program : program }
804- sizes [path ] = field
805- return field
806- }
807833for _ , section := range sections {
808834switch section .Type {
809835case memoryCode :
810- readSection (section , addresses , func (path string , size uint64 , isVariable bool ) {
811- field := getSize (path )
836+ readSection (section , addresses , program , func (ps * packageSize , isVariable bool ) * uint64 {
812837if isVariable {
813- field .ROData += size
814- } else {
815- field .Code += size
838+ return & ps .ROData
816839}
840+ return & ps .Code
817841}, packagePathMap )
818842case memoryROData :
819- readSection (section , addresses , func (path string , size uint64 , isVariable bool ) {
820- getSize ( path ) .ROData += size
843+ readSection (section , addresses , program , func (ps * packageSize , isVariable bool ) * uint64 {
844+ return & ps .ROData
821845}, packagePathMap )
822846case memoryData :
823- readSection (section , addresses , func (path string , size uint64 , isVariable bool ) {
824- getSize ( path ) .Data += size
847+ readSection (section , addresses , program , func (ps * packageSize , isVariable bool ) * uint64 {
848+ return & ps .Data
825849}, packagePathMap )
826850case memoryBSS :
827- readSection (section , addresses , func (path string , size uint64 , isVariable bool ) {
828- getSize ( path ) .BSS += size
851+ readSection (section , addresses , program , func (ps * packageSize , isVariable bool ) * uint64 {
852+ return & ps .BSS
829853}, packagePathMap )
830854case memoryStack :
831855// We store the C stack as a pseudo-package.
832- getSize ("C stack" ).BSS += section .Size
856+ program .getPackage ("C stack" ).addSize (func (ps * packageSize , isVariable bool ) * uint64 {
857+ return & ps .BSS
858+ }, "" , section .Size , false )
833859}
834860}
835861
@@ -844,8 +870,8 @@ func loadProgramSize(path string, packagePathMap map[string]string) (*programSiz
844870}
845871
846872// readSection determines for each byte in this section to which package it
847- // belongs. It reports this usage through the addSize callback.
848- func readSection (section memorySection , addresses []addressLine , addSize func (string , uint64 , bool ), packagePathMap map [string ]string ) {
873+ // belongs.
874+ func readSection (section memorySection , addresses []addressLine , program * programSize , getField func (* packageSize , bool ) * uint64 , packagePathMap map [string ]string ) {
849875// The addr variable tracks at which address we are while going through this
850876// section. We start at the beginning.
851877addr := section .Address
@@ -867,9 +893,9 @@ func readSection(section memorySection, addresses []addressLine, addSize func(st
867893addrAligned := (addr + line .Align - 1 ) &^ (line .Align - 1 )
868894if line .Align > 1 && addrAligned >= line .Address {
869895// It is, assume that's what causes the gap.
870- addSize ("(padding)" , line .Address - addr , true )
896+ program . getPackage ("(padding)" ). addSize ( getField , " " , line .Address - addr , true )
871897} else {
872- addSize ("(unknown)" , line .Address - addr , false )
898+ program . getPackage ("(unknown)" ). addSize ( getField , " " , line .Address - addr , false )
873899if sizesDebug {
874900fmt .Printf ("%08x..%08x %5d: unknown (gap), alignment=%d\n " , addr , line .Address , line .Address - addr , line .Align )
875901}
@@ -891,7 +917,8 @@ func readSection(section memorySection, addresses []addressLine, addSize func(st
891917length = line .Length - (addr - line .Address )
892918}
893919// Finally, mark this chunk of memory as used by the given package.
894- addSize (findPackagePath (line .File , packagePathMap ), length , line .IsVariable )
920+ packagePath , filename := findPackagePath (line .File , packagePathMap )
921+ program .getPackage (packagePath ).addSize (getField , filename , length , line .IsVariable )
895922addr = line .Address + line .Length
896923}
897924if addr < sectionEnd {
@@ -900,9 +927,9 @@ func readSection(section memorySection, addresses []addressLine, addSize func(st
900927if section .Align > 1 && addrAligned >= sectionEnd {
901928// The gap is caused by the section alignment.
902929// For example, if a .rodata section ends with a non-aligned string.
903- addSize ("(padding)" , sectionEnd - addr , true )
930+ program . getPackage ("(padding)" ). addSize ( getField , " " , sectionEnd - addr , true )
904931} else {
905- addSize ("(unknown)" , sectionEnd - addr , false )
932+ program . getPackage ("(unknown)" ). addSize ( getField , " " , sectionEnd - addr , false )
906933if sizesDebug {
907934fmt .Printf ("%08x..%08x %5d: unknown (end), alignment=%d\n " , addr , sectionEnd , sectionEnd - addr , section .Align )
908935}
@@ -912,17 +939,25 @@ func readSection(section memorySection, addresses []addressLine, addSize func(st
912939
913940// findPackagePath returns the Go package (or a pseudo package) for the given
914941// path. It uses some heuristics, for example for some C libraries.
915- func findPackagePath (path string , packagePathMap map [string ]string ) string {
942+ func findPackagePath (path string , packagePathMap map [string ]string ) ( packagePath , filename string ) {
916943// Check whether this path is part of one of the compiled packages.
917944packagePath , ok := packagePathMap [filepath .Dir (path )]
918- if ! ok {
945+ if ok {
946+ // Directory is known as a Go package.
947+ // Add the file itself as well.
948+ filename = filepath .Base (path )
949+ } else {
919950if strings .HasPrefix (path , filepath .Join (goenv .Get ("TINYGOROOT" ), "lib" )) {
920951// Emit C libraries (in the lib subdirectory of TinyGo) as a single
921- // package, with a "C" prefix. For example: "C compiler-rt" for the
922- // compiler runtime library from LLVM.
923- packagePath = "C " + strings .Split (strings .TrimPrefix (path , filepath .Join (goenv .Get ("TINYGOROOT" ), "lib" )), string (os .PathSeparator ))[1 ]
924- } else if strings .HasPrefix (path , filepath .Join (goenv .Get ("TINYGOROOT" ), "llvm-project" )) {
952+ // package, with a "C" prefix. For example: "C picolibc" for the
953+ // baremetal libc.
954+ libPath := strings .TrimPrefix (path , filepath .Join (goenv .Get ("TINYGOROOT" ), "lib" )+ string (os .PathSeparator ))
955+ parts := strings .SplitN (libPath , string (os .PathSeparator ), 2 )
956+ packagePath = "C " + parts [0 ]
957+ filename = parts [1 ]
958+ } else if prefix := filepath .Join (goenv .Get ("TINYGOROOT" ), "llvm-project" , "compiler-rt" ); strings .HasPrefix (path , prefix ) {
925959packagePath = "C compiler-rt"
960+ filename = strings .TrimPrefix (path , prefix + string (os .PathSeparator ))
926961} else if packageSymbolRegexp .MatchString (path ) {
927962// Parse symbol names like main$alloc or runtime$string.
928963packagePath = path [:strings .LastIndex (path , "$" )]
@@ -945,9 +980,11 @@ func findPackagePath(path string, packagePathMap map[string]string) string {
945980// fixed in the compiler.
946981packagePath = "-"
947982} else {
948- // This is some other path. Not sure what it is, so just emit its directory.
949- packagePath = filepath .Dir (path ) // fallback
983+ // This is some other path. Not sure what it is, so just emit its
984+ // directory as a fallback.
985+ packagePath = filepath .Dir (path )
986+ filename = filepath .Base (path )
950987}
951988}
952- return packagePath
989+ return
953990}
0 commit comments