blob: c0a19bf020dd4fb4132aff0acd36227a0c284bd6 [file] [log] [blame]
/**
* 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"
"os"
"path/filepath"
"sort"
"strconv"
"strings"
"mynewt.apache.org/newt/newt/image"
"mynewt.apache.org/newt/util"
"mynewt.apache.org/newt/newt/interfaces"
)
/*
* These are different memory regions as specified in linker script.
*/
type MemSection struct {
Name string
Offset uint64
EndOff uint64
}
type MemSectionArray []*MemSection
var globalMemSections map[string]*MemSection
func (array MemSectionArray) Len() int {
return len(array)
}
func (array MemSectionArray) Less(i, j int) bool {
return array[i].Offset < array[j].Offset
}
func (array MemSectionArray) Swap(i, j int) {
array[i], array[j] = array[j], array[i]
}
func MakeMemSection(name string, off uint64, size uint64) *MemSection {
memsection := &MemSection{
Name: name,
Offset: off,
EndOff: off + size,
}
return memsection
}
func (m *MemSection) PartOf(addr uint64) bool {
if addr >= m.Offset && addr < m.EndOff {
return true
} else {
return false
}
}
/*
* Info about specific symbol size
*/
type SymbolData struct {
Name string
ObjName string /* Which object file it came from */
Sizes map[string]uint32 /* Sizes indexed by mem section name */
}
type SymbolDataArray []*SymbolData
/*
* We accumulate the size of libraries to elements in this.
*/
type PkgSize struct {
Name string
Sizes map[string]uint32 /* Sizes indexed by mem section name */
Syms map[string]*SymbolData /* Symbols indexed by symbol name */
}
type PkgSizeArray []*PkgSize
func (array PkgSizeArray) Len() int {
return len(array)
}
func (array PkgSizeArray) Less(i, j int) bool {
return array[i].Name < array[j].Name
}
func (array PkgSizeArray) Swap(i, j int) {
array[i], array[j] = array[j], array[i]
}
func (array SymbolDataArray) Len() int {
return len(array)
}
func (array SymbolDataArray) Less(i, j int) bool {
return array[i].Name < array[j].Name
}
func (array SymbolDataArray) Swap(i, j int) {
array[i], array[j] = array[j], array[i]
}
func MakeSymbolData(name string, objName string) *SymbolData {
sym := &SymbolData{
Name: name,
ObjName: objName,
}
sym.Sizes = make(map[string]uint32)
for _, sec := range globalMemSections {
sym.Sizes[sec.Name] = 0
}
return sym
}
func MakePkgSize(name string) *PkgSize {
pkgSize := &PkgSize{
Name: name,
}
pkgSize.Sizes = make(map[string]uint32)
for _, sec := range globalMemSections {
pkgSize.Sizes[sec.Name] = 0
}
pkgSize.Syms = make(map[string]*SymbolData)
return pkgSize
}
func (ps *PkgSize) addSymSize(symName string, objName string, size uint32, addr uint64) {
for _, section := range globalMemSections {
if section.PartOf(addr) {
name := section.Name
size32 := uint32(size)
if size32 > 0 {
sym := ps.Syms[symName]
if sym == nil {
sym = MakeSymbolData(symName, objName)
ps.Syms[symName] = sym
}
ps.Sizes[name] += size32
sym.Sizes[name] += size32
}
break
}
}
}
/*
* Go through GCC generated mapfile, and collect info about symbol sizes
*/
func ParseMapFileSizes(fileName string) (map[string]*PkgSize, error) {
var state int = 0
file, err := os.Open(fileName)
if err != nil {
return nil, util.NewNewtError("Mapfile failed: " + err.Error())
}
var symName string = ""
globalMemSections = make(map[string]*MemSection)
pkgSizes := make(map[string]*PkgSize)
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 {
return nil, util.NewNewtError("Can't parse mem info")
}
size, err := strconv.ParseUint(array[2], 0, 64)
if err != nil {
return nil, util.NewNewtError("Can't parse mem info")
}
globalMemSections[array[0]] = MakeMemSection(array[0], offset,
size)
case 3:
if strings.Contains(scanner.Text(),
"Linker script and memory map") {
state = 4
}
case 4:
var addrStr string = ""
var sizeStr string = ""
var srcFile string = ""
if strings.Contains(scanner.Text(), "/DISCARD/") ||
strings.HasPrefix(scanner.Text(), "OUTPUT(") {
/*
* After this there is only discarded symbols
*/
state = 5
continue
}
array := strings.Fields(scanner.Text())
switch len(array) {
case 1:
/*
* section name on it's own, e.g.
* *(.text*)
*
* section name + symbol name, e.g.
* .text.Reset_Handler
*
* ignore these for now
*/
symName = array[0]
continue
case 2:
/*
* Either stuff from beginning to first useful data e.g.
* END GROUP
*
* or address of symbol + symbol name, e.g.
* 0x00000000080002c8 SystemInit
*
* or section names with multiple input things, e.g.
* *(.ARM.extab* .gnu.linkonce.armextab.*)
*
* or space set aside in linker script e.g.
* 0x0000000020002e80 0x400
* (that's the initial stack)
*
* ignore these for now
*/
continue
case 3:
/*
* address, size, and name of file, e.g.
* 0x000000000800bb04 0x1050 /Users/marko/foo/tadpole/hw//mcu/stm/stm32f3xx/bin/blinky_f3/libstm32f3xx.a(stm32f30x_syscfg.o)
*
* padding, or empty areas defined in linker script:
* *fill* 0x000000000800cb71 0x3
*
* output section name, location, size, e.g.:
* .bss 0x0000000020000ab0 0x23d0
*/
/*
* Record addr, size and name to find library.
*/
if array[0] == "*fill*" {
addrStr = array[1]
sizeStr = array[2]
srcFile = array[0]
symName = array[0]
} else {
addrStr = array[0]
sizeStr = array[1]
srcFile = array[2]
}
case 4:
/*
* section, address, size, name of file, e.g.
* COMMON 0x0000000020002d28 0x8 /Users/marko/foo/tadpole/libs//os/bin/blinky_f3/libos.a(os_arch_arm.o)
*
* linker script symbol definitions:
* 0x0000000020002e80 _ebss = .
*
* crud, e.g.:
* 0x8 (size before relaxing)
*/
symName = array[0]
addrStr = array[1]
sizeStr = array[2]
srcFile = array[3]
default:
continue
}
addr, err := strconv.ParseUint(addrStr, 0, 64)
if err != nil {
continue
}
size, err := strconv.ParseUint(sizeStr, 0, 64)
if err != nil {
continue
}
if size == 0 {
continue
}
// srcFile might be : mylib.a(object_file.o) or object_file.o
tmpStrArr := strings.Split(srcFile, "(")
srcLib := tmpStrArr[0]
objName := ""
if srcLib != "*fill*" {
if len(tmpStrArr) > 1 {
tmpStrArr = strings.Split(tmpStrArr[1], ")")
objName = tmpStrArr[0]
} else {
objName = filepath.Base(tmpStrArr[0])
}
}
tmpStrArr = strings.Split(symName, ".")
if len(tmpStrArr) > 2 {
if tmpStrArr[1] == "rodata" && tmpStrArr[2] == "str1" {
symName = ".rodata.str1"
} else {
symName = tmpStrArr[2]
}
}
pkgSize := pkgSizes[srcLib]
if pkgSize == nil {
pkgSize = MakePkgSize(srcLib)
pkgSizes[srcLib] = pkgSize
}
pkgSize.addSymSize(symName, objName, uint32(size), addr)
symName = ".unknown"
default:
}
}
file.Close()
for name, section := range globalMemSections {
util.StatusMessage(util.VERBOSITY_VERBOSE, "Mem %s: 0x%x-0x%x\n",
name, section.Offset, section.EndOff)
}
return pkgSizes, nil
}
/*
* Return a printable string containing size data for the libraries
*/
func PrintSizes(libs map[string]*PkgSize) error {
/*
* Order sections by offset, and display lib sizes in that order.
*/
memSections := make(MemSectionArray, len(globalMemSections))
var i int = 0
for _, sec := range globalMemSections {
memSections[i] = sec
i++
}
sort.Sort(memSections)
/*
* Order libraries by name, and display them in that order.
*/
pkgSizes := make(PkgSizeArray, len(libs))
i = 0
for _, es := range libs {
pkgSizes[i] = es
i++
}
sort.Sort(pkgSizes)
for _, sec := range memSections {
fmt.Printf("%7s ", sec.Name)
}
fmt.Printf("\n")
for _, es := range pkgSizes {
for i := 0; i < len(memSections); i++ {
fmt.Printf("%7d ", es.Sizes[memSections[i].Name])
}
fmt.Printf("%s\n", filepath.Base(es.Name))
}
return nil
}
func (t *TargetBuilder) Size() error {
err := t.PrepBuild()
if err != nil {
return err
}
fmt.Printf("Size of Application Image: %s\n", t.AppBuilder.buildName)
err = t.AppBuilder.Size()
if err == nil {
if t.LoaderBuilder != nil {
fmt.Printf("Size of Loader Image: %s\n", t.LoaderBuilder.buildName)
err = t.LoaderBuilder.Size()
}
}
return err
}
func (b *Builder) FindPkgNameByArName(arName string) string {
for rpkg, bpkg := range b.PkgMap {
if b.ArchivePath(bpkg) == arName {
return rpkg.Lpkg.FullName()
}
}
return filepath.Base(arName)
}
func (b *Builder) PkgSizes() (*image.ImageManifestSizeCollector, error) {
if b.appPkg == nil {
return nil, util.NewNewtError("app package not specified for this target")
}
if b.targetBuilder.bspPkg.Arch == "sim" {
return nil, util.NewNewtError("'newt size' not supported for sim targets")
}
mapFile := b.AppElfPath() + ".map"
libs, err := ParseMapFileSizes(mapFile)
if err != nil {
return nil, err
}
/*
* Order libraries by name.
*/
pkgSizes := make(PkgSizeArray, len(libs))
i := 0
for _, es := range libs {
pkgSizes[i] = es
i++
}
sort.Sort(pkgSizes)
c := image.NewImageManifestSizeCollector()
for _, es := range pkgSizes {
p := c.AddPkg(b.FindPkgNameByArName(es.Name))
/*
* Order symbols by name.
*/
symbols := make(SymbolDataArray, len(es.Syms))
i := 0
for _, sym := range es.Syms {
symbols[i] = sym
i++
}
sort.Sort(symbols)
for _, sym := range symbols {
for area, areaSz := range sym.Sizes {
if areaSz != 0 {
p.AddSymbol(sym.ObjName, sym.Name, area, areaSz)
}
}
}
}
return c, nil
}
func (b *Builder) Size() error {
if b.appPkg == nil {
return util.NewNewtError("app package not specified for this target")
}
err := b.targetBuilder.PrepBuild()
if err != nil {
return err
}
if b.targetBuilder.bspPkg.Arch == "sim" {
fmt.Println("'newt size' not supported for sim targets.")
return nil
}
mapFile := b.AppElfPath() + ".map"
pkgSizes, err := ParseMapFileSizes(mapFile)
if err != nil {
return err
}
err = PrintSizes(pkgSizes)
if err != nil {
return err
}
c, err := b.newCompiler(b.appPkg, b.FileBinDir(b.AppElfPath()))
if err != nil {
return err
}
fmt.Printf("\nobjsize\n")
output, err := c.PrintSize(b.AppElfPath())
if err != nil {
return err
}
fmt.Printf("%s", output)
return nil
}
func (t *TargetBuilder) SizeReport(sectionName string) error {
err := t.PrepBuild()
if err != nil {
return err
}
fmt.Printf("Size of Application Image: %s\n", t.AppBuilder.buildName)
err = t.AppBuilder.SizeReport(sectionName)
if err == nil {
if t.LoaderBuilder != nil {
fmt.Printf("Size of Loader Image: %s\n", t.LoaderBuilder.buildName)
err = t.LoaderBuilder.SizeReport(sectionName)
}
}
return err
}
func (b *Builder) SizeReport(sectionName string) error {
srcBase := interfaces.GetProject().Path() + "/"
err := SizeReport(b.AppElfPath(), srcBase, sectionName)
if err != nil {
return util.NewNewtError(err.Error())
}
return nil
}