You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mynewt.apache.org by ma...@apache.org on 2017/03/06 21:44:18 UTC
[18/41] incubator-mynewt-newt git commit: newt: More detailed size
report command
newt: More detailed size report command
This patch improves the output of the size command. New flags
were added for this purpose:
Flags:
-F, --flash Print FLASH statistics
-R, --ram Print RAM statistics
The size statistics are broken down into a tree-like structure, where
the leaves are symbols and branches are folders and files. For
each tree element there its size in bytes and percentage contribution
to the total size of the memory region.
Path Size %
=============================================
(...)
libc 32 0.49%
baselibc 32 0.49%
src 32 0.49%
malloc.c 32 0.49%
__malloc_head 24 0.37%
malloc_lock 4 0.06%
malloc_unlock 4 0.06%
(...)
Moreover, there is more memory region info available after specifying
verbose flag(-v):
Mem FLASH: 0x00008000-0x00042000
Mem RAM: 0x20000000-0x20010000
Mem: FLASH
Name Size
.ARM.extab 0
.ARM.exidx 24
.imghdr 32
.text 18624
Total 18680
Mem: RAM
Name Size
.bss 5656
.stack_dummy 432
.vector_relocation 216
.data 256
.bssnz 0
Total 6560
Project: http://git-wip-us.apache.org/repos/asf/incubator-mynewt-newt/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-mynewt-newt/commit/6493620b
Tree: http://git-wip-us.apache.org/repos/asf/incubator-mynewt-newt/tree/6493620b
Diff: http://git-wip-us.apache.org/repos/asf/incubator-mynewt-newt/diff/6493620b
Branch: refs/heads/mynewt_1_0_0
Commit: 6493620b088701568d663fae65f67e9b418259f9
Parents: ffdbc54
Author: Micha\u0142 Narajowski <mi...@codecoup.pl>
Authored: Tue Feb 7 12:34:37 2017 +0100
Committer: Marko Kiiskila <ma...@runtime.io>
Committed: Mon Mar 6 13:36:01 2017 -0800
----------------------------------------------------------------------
newt/builder/size.go | 34 +++-
newt/builder/size_report.go | 332 +++++++++++++++++++++++++++++++++++++++
newt/builder/symbol_tree.go | 194 +++++++++++++++++++++++
newt/cli/build_cmds.go | 18 ++-
4 files changed, 574 insertions(+), 4 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-mynewt-newt/blob/6493620b/newt/builder/size.go
----------------------------------------------------------------------
diff --git a/newt/builder/size.go b/newt/builder/size.go
index 970db2d..5ce2939 100644
--- a/newt/builder/size.go
+++ b/newt/builder/size.go
@@ -106,8 +106,8 @@ func MakePkgSize(name string, memSections map[string]*MemSection) *PkgSize {
/*
* Go through GCC generated mapfile, and collect info about symbol sizes
*/
-func ParseMapFileSizes(fileName string) (map[string]*PkgSize, map[string]*MemSection,
- error) {
+func ParseMapFileSizes(fileName string) (map[string]*PkgSize,
+ map[string]*MemSection, error) {
var state int = 0
file, err := os.Open(fileName)
@@ -375,3 +375,33 @@ func (b *Builder) Size() error {
return nil
}
+
+func (t *TargetBuilder) SizeReport(ram, flash bool) error {
+
+ err := t.PrepBuild()
+
+ if err != nil {
+ return err
+ }
+
+ fmt.Printf("Size of Application Image: %s\n", t.AppBuilder.buildName)
+ err = t.AppBuilder.SizeReport(ram, flash)
+
+ if err == nil {
+ if t.LoaderBuilder != nil {
+ fmt.Printf("Size of Loader Image: %s\n", t.LoaderBuilder.buildName)
+ err = t.LoaderBuilder.SizeReport(ram, flash)
+ }
+ }
+
+ return err
+}
+
+func (b *Builder) SizeReport(ram, flash bool) error {
+ srcBase := b.targetBuilder.GetTarget().App().Repo().Path() + "/"
+ err := SizeReport(b.AppElfPath(), srcBase, ram, flash)
+ if err != nil {
+ return util.NewNewtError(err.Error())
+ }
+ return nil
+}
http://git-wip-us.apache.org/repos/asf/incubator-mynewt-newt/blob/6493620b/newt/builder/size_report.go
----------------------------------------------------------------------
diff --git a/newt/builder/size_report.go b/newt/builder/size_report.go
new file mode 100644
index 0000000..5c9fa02
--- /dev/null
+++ b/newt/builder/size_report.go
@@ -0,0 +1,332 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package builder
+
+import (
+ "bufio"
+ "fmt"
+ "mynewt.apache.org/newt/util"
+ "os"
+ "os/exec"
+ "strconv"
+ "strings"
+)
+
+func runNmCommand(elfFilePath string) ([]byte, error) {
+ var (
+ cmdOut []byte
+ err error
+ )
+ cmdName := "arm-none-eabi-nm"
+ cmdArgs := []string{elfFilePath, "-S", "-l", "--size-sort", "--radix=d"}
+
+ if cmdOut, err = exec.Command(cmdName, cmdArgs...).Output(); err != nil {
+ fmt.Fprintln(os.Stderr, "There was an error running nm command: ", err)
+ os.Exit(1)
+ }
+
+ return cmdOut, err
+}
+
+func runObjdumpCommand(elfFilePath string, params string) ([]byte, error) {
+ var (
+ cmdOut []byte
+ err error
+ )
+ cmdName := "arm-none-eabi-objdump"
+ cmdArgs := []string{params, elfFilePath}
+ if cmdOut, err = exec.Command(cmdName, cmdArgs...).Output(); err != nil {
+ fmt.Fprintln(os.Stderr, "There was an error running objdump command: ",
+ err)
+ os.Exit(1)
+ }
+
+ return cmdOut, err
+}
+
+func loadSymbolsAndPaths(elfFilePath, pathToStrip string) (map[string]string,
+ error) {
+ symbolsPath := make(map[string]string)
+
+ nmOut, err := runNmCommand(elfFilePath)
+ if err != nil {
+ return nil, err
+ }
+
+ lines := strings.Split(string(nmOut), "\n")
+
+ for _, line := range lines {
+ fields := strings.Fields(strings.Replace(line, "\t", " ", -1))
+ if len(fields) < 4 {
+ continue
+ }
+ var path string
+
+ if len(fields) < 5 {
+ path = "(other)"
+ } else {
+ path = strings.Split(fields[4], ":")[0]
+ }
+ if pathToStrip != "" {
+ if strings.Contains(path, pathToStrip) {
+ path = strings.Replace(path, pathToStrip, "", -1)
+ } else {
+ path = "(other)"
+ }
+ }
+ symbolsPath[fields[3]] = path
+ }
+ return symbolsPath, nil
+}
+
+func MakeSymbol(name string, section string, size uint64) *Symbol {
+ symbol := &Symbol{
+ name,
+ section,
+ size,
+ }
+ return symbol
+}
+
+type MemoryRegion struct {
+ Name string
+ Offset uint64
+ EndOff uint64
+ TotalSize uint64
+ SectionNames map[string]struct{}
+ NamesSizes map[string]uint64
+}
+
+func MakeMemoryRegion() *MemoryRegion {
+ section := &MemoryRegion{
+ "", 0, 0, 0,
+ make(map[string]struct{}),
+ make(map[string]uint64),
+ }
+ return section
+}
+
+func (m *MemoryRegion) PartOf(addr uint64) bool {
+ return addr >= m.Offset && addr < m.EndOff
+}
+
+func loadSymbolsAndSections(elfFilePath string) (map[string]*Symbol, error) {
+ objdumpOut, err := runObjdumpCommand(elfFilePath, "-tw")
+ if err != nil {
+ return nil, err
+ }
+
+ lines := strings.Split(string(objdumpOut), "\n")
+ symbols := make(map[string]*Symbol)
+ for _, line := range lines {
+ fields := strings.Fields(strings.Replace(line, "\t", " ", -1))
+
+ if len(fields) == 5 {
+ size, err := strconv.ParseUint(fields[3], 16, 64)
+ if err != nil {
+ continue
+ }
+ symbols[fields[4]] = MakeSymbol(fields[4], fields[2], size)
+ } else if len(fields) == 6 {
+ size, err := strconv.ParseUint(fields[4], 16, 64)
+ if err != nil {
+ continue
+ }
+ symbols[fields[5]] = MakeSymbol(fields[5], fields[3], size)
+ }
+
+ }
+
+ return symbols, nil
+}
+
+func generateMemoryRegions(elfFilePath string) (*MemoryRegion, *MemoryRegion,
+ error) {
+
+ mapFile := elfFilePath + ".map"
+ flashRegion, ramRegion, err := parseMapFileRegions(mapFile)
+ if err != nil {
+ return nil, nil, err
+ }
+
+ objdumpOut, err := runObjdumpCommand(elfFilePath, "-hw")
+ if err != nil {
+ return nil, nil, err
+ }
+
+ lines := strings.Split(string(objdumpOut), "\n")
+ for _, line := range lines {
+ fields := strings.Fields(line)
+ if len(fields) < 7 {
+ continue
+ }
+ size, err := strconv.ParseUint(fields[2], 16, 64)
+ if err != nil {
+ continue
+ }
+ address, err := strconv.ParseUint(fields[3], 16, 64)
+ if err != nil {
+ continue
+ }
+
+ if flashRegion.PartOf(address) {
+ flashRegion.TotalSize += size
+ flashRegion.SectionNames[fields[1]] = struct{}{}
+ flashRegion.NamesSizes[fields[1]] = size
+ continue
+ }
+
+ if ramRegion.PartOf(address) {
+ ramRegion.TotalSize += size
+ ramRegion.SectionNames[fields[1]] = struct{}{}
+ ramRegion.NamesSizes[fields[1]] = size
+ continue
+ }
+ }
+
+ return flashRegion, ramRegion, nil
+}
+
+/*
+ * Go through GCC generated mapfile, and collect info about symbol sizes
+ */
+func parseMapFileRegions(fileName string) (*MemoryRegion, *MemoryRegion,
+ error) {
+ var state int = 0
+
+ file, err := os.Open(fileName)
+ if err != nil {
+ return nil, nil, err
+ }
+
+ flashRegion := MakeMemoryRegion()
+ ramRegion := MakeMemoryRegion()
+
+ scanner := bufio.NewScanner(file)
+ for scanner.Scan() {
+ switch state {
+ case 0:
+ if strings.Contains(scanner.Text(), "Memory Configuration") {
+ state = 1
+ }
+ case 1:
+ if strings.Contains(scanner.Text(), "Origin") {
+ state = 2
+ }
+ case 2:
+ if strings.Contains(scanner.Text(), "*default*") {
+ state = 3
+ continue
+ }
+ array := strings.Fields(scanner.Text())
+ offset, err := strconv.ParseUint(array[1], 0, 64)
+ if err != nil {
+ continue
+ }
+ size, err := strconv.ParseUint(array[2], 0, 64)
+ if err != nil {
+ continue
+ }
+ if strings.EqualFold(array[0], "flash") {
+ flashRegion.Name = array[0]
+ flashRegion.Offset = offset
+ flashRegion.EndOff = offset + size
+ } else if strings.EqualFold(array[0], "ram") {
+ ramRegion.Name = array[0]
+ ramRegion.Offset = offset
+ ramRegion.EndOff = offset + size
+ }
+ case 3:
+ fallthrough
+ default:
+ return flashRegion, ramRegion, nil
+ }
+
+ }
+ return flashRegion, flashRegion, nil
+}
+
+func logMemoryRegionStats(flashRegion, ramRegion *MemoryRegion) {
+ util.StatusMessage(util.VERBOSITY_VERBOSE, "%-10s 0x%08x-0x%08x\n",
+ "Mem FLASH:", flashRegion.Offset, flashRegion.EndOff)
+ util.StatusMessage(util.VERBOSITY_VERBOSE, "%-10s 0x%08x-0x%08x\n",
+ "Mem RAM:", ramRegion.Offset, ramRegion.EndOff)
+ util.StatusMessage(util.VERBOSITY_VERBOSE, "\n")
+ util.StatusMessage(util.VERBOSITY_VERBOSE, "Mem: FLASH\n")
+ util.StatusMessage(util.VERBOSITY_VERBOSE, "%-20s %10s\n", "Name", "Size")
+ for sectionName, size := range flashRegion.NamesSizes {
+ util.StatusMessage(util.VERBOSITY_VERBOSE, "%-20s %10d\n",
+ sectionName, size)
+ }
+ util.StatusMessage(util.VERBOSITY_VERBOSE, "%-20s %10d\n", "Total",
+ flashRegion.TotalSize)
+ util.StatusMessage(util.VERBOSITY_VERBOSE, "\n")
+ util.StatusMessage(util.VERBOSITY_VERBOSE, "Mem: RAM\n")
+ util.StatusMessage(util.VERBOSITY_VERBOSE, "%-20s %10s\n", "Name", "Size")
+ for sectionName, size := range ramRegion.NamesSizes {
+ util.StatusMessage(util.VERBOSITY_VERBOSE, "%-20s %10d\n",
+ sectionName, size)
+ }
+ util.StatusMessage(util.VERBOSITY_VERBOSE, "%-20s %10d\n", "Total",
+ ramRegion.TotalSize)
+ util.StatusMessage(util.VERBOSITY_VERBOSE, "\n")
+}
+
+func SizeReport(elfFilePath, srcBase string, ram bool, flash bool) error {
+ symbolsPath, err := loadSymbolsAndPaths(elfFilePath, srcBase)
+ if err != nil {
+ return err
+ }
+ loadedSectionSizes, err := loadSymbolsAndSections(elfFilePath)
+ if err != nil {
+ return err
+ }
+ flashRegion, ramRegion, err := generateMemoryRegions(elfFilePath)
+ if err != nil {
+ return err
+ }
+
+ logMemoryRegionStats(flashRegion, ramRegion)
+
+ startPath := "."
+
+ if flash {
+ flashNodes := newFolder(startPath)
+ for _, symbol := range loadedSectionSizes {
+ if _, ok := flashRegion.SectionNames[symbol.Section]; ok {
+ flashNodes.addSymbol(symbol, symbolsPath[symbol.Name])
+ }
+ }
+ fmt.Println("FLASH report:")
+ fmt.Printf("%v", flashNodes.ToString(flashRegion.TotalSize))
+ }
+
+ if ram {
+ ramNodes := newFolder(startPath)
+ for _, symbol := range loadedSectionSizes {
+ if _, ok := ramRegion.SectionNames[symbol.Section]; ok {
+ ramNodes.addSymbol(symbol, symbolsPath[symbol.Name])
+ }
+ }
+ fmt.Println("RAM report:")
+ fmt.Printf("%v", ramNodes.ToString(ramRegion.TotalSize))
+ }
+ return nil
+}
http://git-wip-us.apache.org/repos/asf/incubator-mynewt-newt/blob/6493620b/newt/builder/symbol_tree.go
----------------------------------------------------------------------
diff --git a/newt/builder/symbol_tree.go b/newt/builder/symbol_tree.go
new file mode 100644
index 0000000..ea6202b
--- /dev/null
+++ b/newt/builder/symbol_tree.go
@@ -0,0 +1,194 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package builder
+
+import (
+ "fmt"
+ "sort"
+ "strings"
+)
+
+type Symbol struct {
+ Name string
+ Section string
+ Size uint64
+}
+
+type File struct {
+ Name string
+ Symbols map[string]*Symbol
+}
+
+type Folder struct {
+ Name string
+ Files map[string]*File
+ Folders map[string]*Folder
+}
+
+func newFolder(name string) *Folder {
+ return &Folder{name, make(map[string]*File), make(map[string]*Folder)}
+}
+
+func newFile(name string) *File {
+ return &File{name, make(map[string]*Symbol)}
+}
+
+func (f *File) sumSize() uint64 {
+ var sum uint64
+ for _, symbol := range f.Symbols {
+ sum += symbol.Size
+ }
+ return sum
+}
+
+func (f *Folder) sumSize() uint64 {
+ var sum uint64
+ for _, folder := range f.Folders {
+ sum += folder.sumSize()
+ }
+
+ for _, file := range f.Files {
+ sum += file.sumSize()
+ }
+ return sum
+}
+
+func (f *Folder) getFolder(name string) *Folder {
+ if nextF, ok := f.Folders[name]; ok {
+ return nextF
+ } else {
+ f.Folders[name] = newFolder(name)
+ return f.Folders[name]
+ }
+ return &Folder{} // cannot happen
+}
+
+func (f *Folder) getFile(name string) *File {
+ if nextF, ok := f.Files[name]; ok {
+ return nextF
+ } else {
+ f.Files[name] = newFile(name)
+ return f.Files[name]
+ }
+ return &File{} // cannot happen
+}
+
+func (f *File) getSymbol(name string) *Symbol {
+ if nextF, ok := f.Symbols[name]; ok {
+ return nextF
+ } else {
+ f.Symbols[name] = &Symbol{name, "", 0}
+ return f.Symbols[name]
+ }
+ return &Symbol{} // cannot happen
+}
+
+func (f *Folder) addFolder(path []string) *Folder {
+ if len(path) == 1 {
+ // last segment == new folder
+ return f.getFolder(path[0])
+ } else {
+ return f.getFolder(path[0]).addFolder(path[1:])
+ }
+}
+
+func (f *Folder) addFile(path []string) *File {
+ if len(path) == 1 {
+ // last segment == file
+ return f.getFile(path[0])
+ } else {
+ return f.getFolder(path[0]).addFile(path[1:])
+ }
+}
+
+func (f *Folder) addSymbol(symbol *Symbol, path string) *Symbol {
+ segments := strings.Split(path, "/")
+ file := f.addFile(segments)
+ sym := file.getSymbol(symbol.Name)
+ sym.Section = symbol.Section
+ sym.Size += symbol.Size
+ return sym
+}
+
+func (f *File) String(indent string, level int, total uint64) string {
+ var str string
+ if f.sumSize() <= 0 {
+ return ""
+ }
+ size := f.sumSize()
+ percent := 100 * float64(size) / float64(total)
+ str += fmt.Sprintf("%-80s %20d %8.2f%%\n", strings.Repeat(indent, level)+
+ f.Name, size, percent)
+
+ var sorted []string
+ for symName := range f.Symbols {
+ sorted = append(sorted, symName)
+ }
+ sort.Strings(sorted)
+ for _, sym := range sorted {
+ size := f.Symbols[sym].Size
+ percent := 100 * float64(size) / float64(total)
+ if f.Symbols[sym].Size > 0 {
+ str += fmt.Sprintf("%-80s %20d %8.2f%%\n",
+ strings.Repeat(indent, level+1)+
+ f.Symbols[sym].Name,
+ size, percent)
+ }
+ }
+ return str
+}
+
+func (f *Folder) StringRec(indent string, level int, total uint64) string {
+ var str string
+
+ var sorted []string
+ for folderName := range f.Folders {
+ sorted = append(sorted, folderName)
+ }
+ for fileName := range f.Files {
+ sorted = append(sorted, fileName)
+ }
+ sort.Strings(sorted)
+
+ for _, name := range sorted {
+ if folder, ok := f.Folders[name]; ok {
+ size := folder.sumSize()
+ percent := 100 * float64(size) / float64(total)
+ str += fmt.Sprintf("%-80s %20d %8.2f%%\n",
+ strings.Repeat(indent, level)+folder.Name, size, percent)
+ str += folder.StringRec(indent, level+1, total)
+ } else {
+ str += f.Files[name].String(indent, level, total)
+ }
+ }
+ return str
+}
+
+func (f *Folder) ToString(total uint64) string {
+ indent := " "
+ var str string
+ str += fmt.Sprintf("%-90s %10s %9s\n", "Path", "Size", "%")
+ str += strings.Repeat("=", 111) + "\n"
+ str += f.StringRec(indent, 0, total)
+ str += strings.Repeat("=", 111) + "\n"
+ str += fmt.Sprintf("%90s %10d\n",
+ "Total symbol size (i.e. excluding padding, etc.)", f.sumSize())
+ return str
+}
http://git-wip-us.apache.org/repos/asf/incubator-mynewt-newt/blob/6493620b/newt/cli/build_cmds.go
----------------------------------------------------------------------
diff --git a/newt/cli/build_cmds.go b/newt/cli/build_cmds.go
index 43cf47e..f0789db 100644
--- a/newt/cli/build_cmds.go
+++ b/newt/cli/build_cmds.go
@@ -358,7 +358,7 @@ func debugRunCmd(cmd *cobra.Command, args []string) {
}
}
-func sizeRunCmd(cmd *cobra.Command, args []string) {
+func sizeRunCmd(cmd *cobra.Command, args []string, ram bool, flash bool) {
if len(args) < 1 {
NewtUsage(cmd, util.NewNewtError("Must specify target"))
}
@@ -375,6 +375,13 @@ func sizeRunCmd(cmd *cobra.Command, args []string) {
NewtUsage(nil, err)
}
+ if ram || flash {
+ if err := b.SizeReport(ram, flash); err != nil {
+ NewtUsage(cmd, err)
+ }
+ return
+ }
+
if err := b.Size(); err != nil {
NewtUsage(cmd, err)
}
@@ -452,13 +459,20 @@ func AddBuildCommands(cmd *cobra.Command) {
sizeHelpText := "Calculate the size of target components specified by " +
"<target-name>."
+ var ram, flash bool
sizeCmd := &cobra.Command{
Use: "size <target-name>",
Short: "Size of target components",
Long: sizeHelpText,
- Run: sizeRunCmd,
+ Run: func(cmd *cobra.Command, args []string) {
+ sizeRunCmd(cmd, args, ram, flash)
+ },
}
+ sizeCmd.Flags().BoolVarP(&ram, "ram", "R", false, "Print RAM statistics")
+ sizeCmd.Flags().BoolVarP(&flash, "flash", "F", false,
+ "Print FLASH statistics")
+
cmd.AddCommand(sizeCmd)
AddTabCompleteFn(sizeCmd, targetList)
}