欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Go語言編程通過dwarf獲取內(nèi)聯(lián)函數(shù)

 更新時間:2022年11月23日 10:50:45   作者:用戶楊杰  
這篇文章主要為大家介紹了Go語言編程通過dwarf獲取內(nèi)聯(lián)函數(shù)詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪

dwarf組成

dwarf 由 The Debugging Information Entry 。

type Entry struct {
    Offset   Offset
    Tag      Tag // 描述其類型
    Children bool
    Field    []Field // 包含的字段
}

不同的 entry 有不同的類型:

  • tag compile unit, 在 go 中就表示一個 package 下的所有源代碼文件。
  • tag sub program, 表示函數(shù)

一個 entry 有不同的 attr:

  • AT_low_pc, AT_high_pc 分別代表函數(shù)的 起始/結(jié)束 PC地址
  • AttrName 表示名字

對于函數(shù):

package s
    func Leaf(lx, ly int) int {
        return (lx << 7) ^ (ly >> uint32(lx&7))
    }
    func Top(tq int) int {
        var tv [10]int
        tr := Leaf(tq-13, tq+13)
        return tr + tv[tr&3]
    }

對應(yīng)的 entry:

DW_TAG_complication_unit{ // package s
DW_TAG_subprogram {
      DW_AT_name:            s.Top
      DW_TAG_formal_parameter {
         DW_AT_name:         tq  // 參數(shù)名
         DW_AT_type:         ... // 參數(shù)類型
      }  
   }
}   

如何將 addr 轉(zhuǎn)換為行號

  • seekpc 返回該 pc 對應(yīng)的 complication unit。(類似于線性搜索,并且下一次調(diào)用 seekpc,會在上一次的之后開始搜索,所以 pc 最好需要排序)
  • dwarf.Reader.Next() 將會循環(huán)讀取 entry,如果是函數(shù)并且地址在范圍內(nèi),就認為找到了對應(yīng) address 的函數(shù)名。
  • dwarf line reader 將會返回該 complication unit 對應(yīng)的 line 信息。
// go1.19/src/cmd/pprof/pprof.go:300
func pctoLine(f *elf.File, pc uint64) []driver.Frame {
	dwarf, _ := f.DWARF()
	r := dwarf.Reader()
	unit, _ := r.SeekPC(pc)
	lines, _ := dwarf.LineReader(unit)
	var lentry godwarf.LineEntry
	if err := lines.SeekPC(pc, &lentry); err != nil {
		log.Fatal(err)
	}
	// Try to find the function name.
	name := ""
FindName:
	for entry, err := r.Next(); entry != nil && err == nil; entry, err = r.Next() {
		if entry.Tag == godwarf.TagSubprogram {
			ranges, err := dwarf.Ranges(entry)
			if err != nil {
				log.Fatal(err)
			}
			for _, pcs := range ranges {
				if pcs[0] <= pc && pc < pcs[1] {
					var ok bool
					// TODO: AT_linkage_name, AT_MIPS_linkage_name.
					name, ok = entry.Val(godwarf.AttrName).(string)
					if ok {
						break FindName
					}
				}
			}
		}
	}
	frames := []driver.Frame{
		{
			Func: name,
			File: lentry.File.Name,
			Line: lentry.Line,
		},
	}
	return frames
}

內(nèi)聯(lián)函數(shù)

使用 pprof 獲取地址:

其中 simplify1 是被內(nèi)聯(lián)的函數(shù)。

2152: 0x5a51a8 M=1 regexp/syntax.simplify1 /usr/local/go1.18/go/src/regexp/syntax/simplify.go:148 s=0
             regexp/syntax.(*Regexp).Simplify /usr/local/go1.18/go/src/regexp/syntax/simplify.go:100 s=0

調(diào)用棧:

使用正常的方式獲取地址:

被內(nèi)聯(lián)的函數(shù)消失了,但是行號還是正確的。

regexp/syntax.(*Regexp).Simplify /usr/local/go1.18/go/src/regexp/syntax/simplify.go 148

如何展開內(nèi)聯(lián)函數(shù)

inline 內(nèi)聯(lián)設(shè)計:go.googlesource.com/proposal/+/…

如果我們有一個函數(shù):

package s
    func Leaf(lx, ly int) int {
        return (lx << 7) ^ (ly >> uint32(lx&7))
    }
    func Top(tq int) int {
        var tv [10]int
        tr := Leaf(tq-13, tq+13)
        return tr + tv[tr&3]
    }

那么對于 top 這個程序,我們會包含以下 entry:

  • tag_subprogram: 表示 top 這個函數(shù)
  • tag_subprogram: 表示 leaf 這個內(nèi)聯(lián)函數(shù)的抽象(含有函數(shù)名,不含有地址范圍)
  • TAG_inlined_subroutine: 表示 leaf 這個內(nèi)聯(lián)函數(shù)的實體。(包含地址范圍等信息)
DW_TAG_subprogram {
      DW_AT_name:            s.Top
      DW_TAG_formal_parameter {
         DW_AT_name:         tq
         DW_AT_type:         ...
      }
// abstract inline function
DW_TAG_subprogram {   // offset: D1
      DW_AT_name:            s.Leaf
      DW_AT_inline : DW_INL_inlined (not declared as inline but inlined)
      ...
      DW_TAG_formal_parameter {   // offset: D2
         DW_AT_name:         lx
         DW_AT_type:         ...
      }
      DW_TAG_formal_parameter {    // offset: D3
         DW_AT_name:         ly
         DW_AT_type:         ...
      }
      ...
   }
      // inlined body of 'Leaf'
      DW_TAG_inlined_subroutine {
         DW_AT_abstract_origin: // reference to D1 above
         DW_AT_call_file: 1
         DW_AT_call_line: 15
         DW_AT_ranges         : ...
         DW_TAG_formal_parameter {
            DW_AT_abstract_origin: // reference to D2 above
            DW_AT_location:        ...
         }
         DW_TAG_formal_parameter {
            DW_AT_abstract_origin: // reference to D3 above
            DW_AT_location:        ...
         }
      }
   }

因此,通過 pc 地址不斷的循環(huán)遍歷 inline_subroutine 這種類型的 entry,我們就可以獲取所有的內(nèi)聯(lián)函數(shù)。

func inlineStackInternal(stack []*godwarf.Tree, n *godwarf.Tree, pc uint64) []*godwarf.Tree {
	switch n.Tag {
	case dwarf.TagSubprogram, dwarf.TagInlinedSubroutine, dwarf.TagLexDwarfBlock:
		if pc == 0 || n.ContainsPC(pc) {
			for _, child := range n.Children {
				stack = inlineStackInternal(stack, child, pc)
			}
			if n.Tag == dwarf.TagInlinedSubroutine {
				stack = append(stack, n)
			}
		}
	}
	return stack
}

然后,我們通過該 entry 的 AbstractOrigin 字段,獲取 abstract function,然后就可以得到函數(shù)名。

abstractOrigin := f.abstractSubprograms[e.Val(dwarf.AttrAbstractOrigin).(dwarf.Offset)]

使用 parca 展開內(nèi)聯(lián)函數(shù)

使用 parca 獲取內(nèi)聯(lián):

package main
import (
	"debug/elf"
	"log"
	"os"
	"strconv"
	parcadwarf "github.com/parca-dev/parca/pkg/symbol/addr2line"
	"github.com/parca-dev/parca/pkg/symbol/demangle"
)
func main() {
	f, _ := elf.Open(os.Args[1])
	debug, _ := parcadwarf.DWARF(nil, f, demangle.NewDemangler("simple", false))
	pc, _ := strconv.ParseUint(os.Args[2], 16, 64)
	log.Println(debug.PCToLines(pc))
}

pprof raw 的輸出,該 address fe1475 總共代表三個函數(shù):

1951: 0xfe1475 M=1 google.golang.org/grpc/metadata.Join /home/gitlab-runner/go/pkg/mod/google.golang.org/grpc@v1.48.0/metadata/metadata.go:141 s=0
             google.golang.org/grpc/metadata.MD.Copy /home/gitlab-runner/go/pkg/mod/google.golang.org/grpc@v1.48.0/metadata/metadata.go:92 s=0
             go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc.UnaryServerInterceptor.func1 /home/gitlab-runner/go/pkg/mod/go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc@v0.33.0/interceptor.go:304 s=0

輸出:

./dwarf/dwarf /home/data/server/otel-collector/data/otelcol-contrib  fe1475
[{297 name:"go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc.UnaryServerInterceptor.func1" filename:"/home/gitlab-runner/go/pkg/mod/go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc@v0.33.0/interceptor.go"} {138 name:"?" filename:"/home/gitlab-runner/go/pkg/mod/google.golang.org/grpc@v1.48.0/metadata/metadata.go"} {92 name:"?" filename:"/home/gitlab-runner/go/pkg/mod/google.golang.org/grpc@v1.48.0/metadata/metadata.go"}]

parca 輸出有以下問題

  • 無法正確的獲取內(nèi)聯(lián)的函數(shù)名
  • 內(nèi)聯(lián)函數(shù)的行號不正確。
  • 內(nèi)聯(lián)函數(shù)順序不對

對于第一個問題,其實是 parca 只會將 pc 地址表示的當(dāng)前 complication unit 進行內(nèi)聯(lián)函數(shù)映射。

對于途中就是 interceptor 這個庫。

而內(nèi)聯(lián)的函數(shù)在 meatadata 這個庫,所以無法正確的獲取函數(shù)名。

對于第二個問題,由于內(nèi)聯(lián)函數(shù)展開后,獲取的是 DW_TAG_subprogram,它映射一個范圍內(nèi)的地址,自然也無法精確的獲取行號。

對于第三個問題,parca 寫錯了。

以上就是Go語言編程通過dwarf獲取內(nèi)聯(lián)函數(shù)的詳細內(nèi)容,更多關(guān)于Go dwarf獲取內(nèi)聯(lián)函數(shù)的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Golang中Error的設(shè)計與實踐詳解

    Golang中Error的設(shè)計與實踐詳解

    這篇文章主要為大家詳細介紹了Golang中Error的設(shè)計以及是具體如何處理錯誤的相關(guān)知識,文中的示例代碼簡潔易懂,需要的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2023-08-08
  • Golang String字符串類型轉(zhuǎn)Json格式

    Golang String字符串類型轉(zhuǎn)Json格式

    本文主要介紹了Golang String字符串類型轉(zhuǎn)Json格式的實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2024-05-05
  • golang解析yaml文件操作

    golang解析yaml文件操作

    這篇文章主要介紹了golang解析yaml文件操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-12-12
  • go語言beego框架jwt身份認證實現(xiàn)示例

    go語言beego框架jwt身份認證實現(xiàn)示例

    這篇文章主要為大家介紹了go語言beego框架jwt身份認證實現(xiàn)示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步早日升職加薪
    2022-04-04
  • golang http使用踩過的坑與應(yīng)對方式

    golang http使用踩過的坑與應(yīng)對方式

    這篇文章主要介紹了golang http使用踩過的坑與應(yīng)對方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-01-01
  • Go高級特性探究之處理1分鐘百萬請求詳解

    Go高級特性探究之處理1分鐘百萬請求詳解

    對于大型的互聯(lián)網(wǎng)應(yīng)用程序,如電商平臺、社交網(wǎng)絡(luò)、金融交易平臺等,每秒鐘都會收到大量的請求,那么Go是如何處理這些百萬請求的呢,下面就來和大家詳細講講
    2023-06-06
  • go語言中decimal的用法詳解

    go語言中decimal的用法詳解

    本文主要介紹了go語言中decimal的用法詳解,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-03-03
  • Go語言中通過Lua腳本操作Redis的方法

    Go語言中通過Lua腳本操作Redis的方法

    這篇文章主要給大家介紹了關(guān)于Go語言中通過Lua腳本操作Redis的方法,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考借鑒,下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧。
    2018-01-01
  • go語言實現(xiàn)二叉樹的序例化與反序列化

    go語言實現(xiàn)二叉樹的序例化與反序列化

    這篇文章主要介紹了go語言實現(xiàn)二叉樹的序例化與反序列化,文章圍繞主題展開詳細內(nèi)容介紹,具有一定的參考價值,需要的小伙伴可以參考一下
    2022-05-05
  • golang中struct和[]byte的相互轉(zhuǎn)換示例

    golang中struct和[]byte的相互轉(zhuǎn)換示例

    這篇文章主要介紹了golang中struct和[]byte的相互轉(zhuǎn)換示例,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-07-07

最新評論