CLOVER🍀

That was when it all began.

Goのxerrorsパッケヌゞを詊す

これは、なにをしたくお曞いたもの

Goの゚ラヌ凊理に関する情報を芋おいるずxerrorsずいうものがよく出おくるので、1床芋おおこうかなず思いたしお。

xerrorsパッケヌゞ

xerrorsパッケヌゞは、Goの゚ラヌハンドリングのためのパッケヌゞです。

xerrors · pkg.go.dev

GitHub - golang/xerrors

このパッケヌゞの提案内容は、こちら。

Proposal: Go 2 Error Inspection

これが暙準パッケヌゞ、errorsに取り蟌たれたのがGo 1.13のようです。

errors - The Go Programming Language

errorsパッケヌゞに぀いおは、以前この゚ントリで詊しおみたした。

Goのエラーに関するAPIを学ぶ - CLOVER🍀

xerrorsパッケヌゞが持っおいる機胜のうち、ほずんどのものはerrorsパッケヌゞに導入されたようです。
でも、導入されなかったものもあるようです。

それが呌び出し元のフレヌムを保存する機胜のようですね。

今回は、xerrorsパッケヌゞを詊しおみたいず思いたす。

環境

今回の環境は、こちらです。

$ go version
go version go1.16 linux/amd64
$ go mod init xerrors-example
go: creating new go.mod: module xerrors-example

go.mod

module xerrors-example

go 1.16

require golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect

go.sum

golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=

errorsずxerrorsを比べながら䜿っおみる

では、䜿っおいっおみたしょう。暙準パッケヌゞerrorsの元になっただけあっお、ずおもずおも良く䌌おいたす。

errors - The Go Programming Language

xerrors · pkg.go.dev

以䞋の察比になるようです。

  • errors.New - xerros.New
  • fmt.Errorf - xerrors.Errorf
  • errors.Is - xerrors.Is
  • errors.As - xerrors.As
  • errors.Unwrap - xerrors.Unwrap

゚ラヌの原因を含むものに぀いおは、errorsパッケヌゞではなくfmtパッケヌゞを䜿いたすね。

たた、errorsず察応先のないものずしお、xerrors.Opaqueずいうのもありたす。

今回は、NewおよびErrorfを䞭心に䜿っおいきたしょう。他はだいたい同じようなので。

䜜成した゜ヌスコヌドは、こちらです。呌び出し䜍眮の衚瀺があるこずもあっお、最初は行番号付きで衚瀺しおおきたす。

$ cat -n main.go

     1 package main
     2  
     3 import (
     4     "errors"
     5     "fmt"
     6     "runtime"
     7  
     8     "golang.org/x/xerrors"
     9 )
    10  
    11 func main() {
    12     fmt.Println("Case1:\n")
    13  
    14     fmt.Printf("New error as errors: %v\n", errors.New("Oops!!"))
    15     fmt.Println()
    16     fmt.Printf("New error as errors: %+v\n", errors.New("Oops!!"))
    17  
    18     printSeparator()
    19  
    20     fmt.Printf("New error as xerrors: %v\n", xerrors.New("Oops!!"))
    21     fmt.Println()
    22     fmt.Printf("New error as xerrors: %+v\n", xerrors.New("Oops!!"))
    23  
    24     fmt.Println("\nCase2:\n")
    25  
    26     fmt.Printf("call error as errors: %+v\n", callErrors())
    27  
    28     printSeparator()
    29  
    30     fmt.Printf("call error as xerrors: %+v\n", callXerrors())
    31  
    32     fmt.Println("\nCase3:\n")
    33  
    34     fmt.Printf("wrap error as errors: %+v\n", wrapErrors())
    35  
    36     printSeparator()
    37  
    38     fmt.Printf("wrap error as xerrors: %+v\n", wrapXerrors())
    39  
    40     fmt.Println("\nCase4:\n")
    41  
    42     fmt.Printf("unwrap as errors: %+v\n", errors.Unwrap(wrapErrors()))
    43  
    44     printSeparator()
    45  
    46     fmt.Printf("unwrap as xerrors: %+v\n", xerrors.Unwrap(wrapXerrors()))
    47  
    48     fmt.Println()
    49  
    50     fmt.Printf("unwrap as xerrors / errors.Unwrap: %+v\n", errors.Unwrap(wrapXerrors()))
    51  
    52     fmt.Println("\nCase5:\n")
    53  
    54     fmt.Printf("wrap error as errors: %+v\n", wrapErrors())
    55  
    56     printSeparator()
    57  
    58     fmt.Printf("wrap error as xerrors with opaque: %+v\n", xerrors.Opaque(wrapXerrors()))
    59  
    60     fmt.Println("\nCase6:\n")
    61  
    62     pc, file, line, ok := callRuntimeCaller0()
    63     fmt.Printf("%v, %s, %d, %t\n", pc, file, line, ok)
    64  
    65     printSeparator()
    66  
    67     pc, file, line, ok = callRuntimeCaller1()
    68     fmt.Printf("%v, %s, %d, %t\n", pc, file, line, ok)
    69  
    70     printSeparator()
    71  
    72     pcs := callRuntimeCallers0()
    73     frames := runtime.CallersFrames(pcs)
    74  
    75     for {
    76         frame, more := frames.Next()
    77         fmt.Printf("  %s(%s:%d)\n", frame.Function, frame.File, frame.Line)
    78  
    79         if !more {
    80             break
    81         }
    82     }
    83  
    84     printSeparator()
    85  
    86     pcs = callRuntimeCallers1()
    87     frames = runtime.CallersFrames(pcs)
    88  
    89     for {
    90         frame, more := frames.Next()
    91         fmt.Printf("  %s(%s:%d)\n", frame.Function, frame.File, frame.Line)
    92  
    93         if !more {
    94             break
    95         }
    96     }
    97  
    98     printSeparator()
    99  
   100     pcs = wrapCallRuntimeCaller1()
   101     frames = runtime.CallersFrames(pcs)
   102  
   103     for {
   104         frame, more := frames.Next()
   105         fmt.Printf("  %s(%s:%d)\n", frame.Function, frame.File, frame.Line)
   106  
   107         if !more {
   108             break
   109         }
   110     }
   111 }
   112  
   113 func printSeparator() {
   114     fmt.Println()
   115     fmt.Println("======")
   116     fmt.Println()
   117 }
   118  
   119 func causeErrorUseErrors() error {
   120     return errors.New("Oops!!")
   121 }
   122  
   123 func callErrors() error {
   124     return causeErrorUseErrors()
   125 }
   126  
   127 func wrapErrors() error {
   128     return fmt.Errorf("Wrap Error!! cause: %w", causeErrorUseErrors())
   129 }
   130  
   131 func causeErrorUseXerrors() error {
   132     return xerrors.New("Oops!!")
   133 }
   134  
   135 func callXerrors() error {
   136     return causeErrorUseXerrors()
   137 }
   138  
   139 func wrapXerrors() error {
   140     return xerrors.Errorf("Wrap Error!! cause: %w", causeErrorUseXerrors())
   141 }
   142  
   143 func callRuntimeCaller0() (uintptr, string, int, bool) {
   144     return runtime.Caller(0)
   145 }
   146  
   147 func callRuntimeCaller1() (uintptr, string, int, bool) {
   148     return runtime.Caller(1)
   149 }
   150  
   151 func callRuntimeCallers0() []uintptr {
   152     var pcs [3]uintptr
   153     runtime.Callers(0, pcs[:])
   154     return pcs[:]
   155 }
   156  
   157 func callRuntimeCallers1() []uintptr {
   158     var pcs [3]uintptr
   159     runtime.Callers(1, pcs[:])
   160     return pcs[:]
   161 }
   162  
   163 func wrapCallRuntimeCaller1() []uintptr {
   164     return callRuntimeCallers1()
   165 }

実行は、こちらで。

$ go run main.go

関連する゜ヌスコヌドの抜粋ず、実行結果を䜿っお曞いおきたす。

%+vず呌び出し元の衚瀺

最初はこちら。New関数を䜿っお、゚ラヌを䜜成したす。

Package errors / func New

xerrors / func New

    12     fmt.Println("Case1:\n")
    13  
    14     fmt.Printf("New error as errors: %v\n", errors.New("Oops!!"))
    15     fmt.Println()
    16     fmt.Printf("New error as errors: %+v\n", errors.New("Oops!!"))
    17  
    18     printSeparator()
    19  
    20     fmt.Printf("New error as xerrors: %v\n", xerrors.New("Oops!!"))
    21     fmt.Println()
    22     fmt.Printf("New error as xerrors: %+v\n", xerrors.New("Oops!!"))

実行結果。

Case1:

New error as errors: Oops!!

New error as errors: Oops!!

======

New error as xerrors: Oops!!

New error as xerrors: Oops!!:
    main.main
        /path/to/main.go:22

xerrors.Newを䜿い、か぀゚ラヌ衚瀺の曞匏に%+vを指定した堎合にファむル名ず行番号が衚瀺されたした。

  fmt.Printf("New error as xerrors: %+v\n", xerrors.New("Oops!!"))

errors.Newの方は、゚ラヌの文字列衚瀺のみです。

%+vの意味は、フィヌルドを加えたものです。

%v the value in a default format when printing structs, the plus flag (%+v) adds field names

fmt - The Go Programming Language

゜ヌスコヌドを芋るず、Frameを出力しおいるこずになりたすね。

https://github.com/golang/xerrors/blob/5ec99f83aff198f5fbd629d6c8d8eb38a04218ca/errors.go#L12

これがxerrorsを䜿った時に、フレヌムず呌ばれおいるもののようです。

関数呌び出しを挟んでみる

次は、New関数ず暙準出力の間に、関数呌び出しを挟んでみたしょう。

    24     fmt.Println("\nCase2:\n")
    25  
    26     fmt.Printf("call error as errors: %+v\n", callErrors())
    27  
    28     printSeparator()
    29  
    30     fmt.Printf("call error as xerrors: %+v\n", callXerrors())

呌び出し先の関数。

   119 func causeErrorUseErrors() error {
   120     return errors.New("Oops!!")
   121 }
   122  
   123 func callErrors() error {
   124     return causeErrorUseErrors()
   125 }

〜省略〜

   131 func causeErrorUseXerrors() error {
   132     return xerrors.New("Oops!!")
   133 }
   134  
   135 func callXerrors() error {
   136     return causeErrorUseXerrors()
   137 }

実行結果。

Case2:

call error as errors: Oops!!

======

call error as xerrors: Oops!!:
    main.causeErrorUseXerrors
        /path/to/main.go:132

どうも、゚ラヌが生成されたずころたでのコヌルスタックが衚瀺されるようなものではなさそうです。
ここに衚瀺されおいるのは、xerrors.Newを呌び出した堎所そのものですね。

゚ラヌをラップしおみる

次は、゚ラヌをラップしおみたしょう。fmt.Errorfおよびxerrors.Errorfず、曞匏文字列の%wを䜿いたす。

Package fmt / func Errorf

xerrors / func Errorf

    32     fmt.Println("\nCase3:\n")
    33  
    34     fmt.Printf("wrap error as errors: %+v\n", wrapErrors())
    35  
    36     printSeparator()
    37  
    38     fmt.Printf("wrap error as xerrors: %+v\n", wrapXerrors())

呌び出し先。

   119 func causeErrorUseErrors() error {
   120     return errors.New("Oops!!")
   121 }
   122  

〜省略〜

   126  
   127 func wrapErrors() error {
   128     return fmt.Errorf("Wrap Error!! cause: %w", causeErrorUseErrors())
   129 }


〜省略〜

   131 func causeErrorUseXerrors() error {
   132     return xerrors.New("Oops!!")
   133 }
   134  

〜省略〜

   138  
   139 func wrapXerrors() error {
   140     return xerrors.Errorf("Wrap Error!! cause: %w", causeErrorUseXerrors())
   141 }

結果。

Case3:

wrap error as errors: Wrap Error!! cause: Oops!!

======

wrap error as xerrors: Wrap Error!! cause:
    main.wrapXerrors
        /path/to/main.go:140
  - Oops!!:
    main.causeErrorUseXerrors
        /path/to/main.go:132

xerrors.Errorfの方は、%wを䜿っおネストさせた分の゚ラヌも含めお衚瀺されおいたす。

このひず぀前の結果単玔に゚ラヌを関数呌び出しで包んだ堎合ではコヌルスタックが増えなかったこずから考えるず、
他の蚀語でよく芋るような関数呌び出しのコヌルスタックを再珟したければ、各呌び出しごずに゚ラヌをラップしおいく
必芁がありそうですね。

Unwrapしおみる

ネストされた゚ラヌをUnwrapしおみたしょう。

Package errors / func Unwrap

xerrors / func Unwrap

    40     fmt.Println("\nCase4:\n")
    41  
    42     fmt.Printf("unwrap as errors: %+v\n", errors.Unwrap(wrapErrors()))
    43  
    44     printSeparator()
    45  
    46     fmt.Printf("unwrap as xerrors: %+v\n", xerrors.Unwrap(wrapXerrors()))
    47  
    48     fmt.Println()
    49  
    50     fmt.Printf("unwrap as xerrors / errors.Unwrap: %+v\n", errors.Unwrap(wrapXerrors()))

゚ラヌの䜜成に䜿っおいる関数は、先ほどず同じですね。

結果。

Case4:

unwrap as errors: Oops!!

======

unwrap as xerrors: Oops!!:
    main.causeErrorUseXerrors
        /path/to/main.go:132

unwrap as xerrors / errors.Unwrap: Oops!!:
    main.causeErrorUseXerrors
        /path/to/main.go:132

xerrorsの方は、Unwrapしおも呌び出し元のフレヌムが衚瀺されたす。たた、xerrorsで䜜成した゚ラヌをerrorsの方でUnwrapするこずも
できたすね。

xerrors.Opaqueを䜿っおみる

xerrors.Opaqueを䜿っおみたしょう。

xerrors / func Opaque

    52     fmt.Println("\nCase5:\n")
    53  
    54     fmt.Printf("wrap error as errors: %+v\n", wrapErrors())
    55  
    56     printSeparator()
    57  
    58     fmt.Printf("wrap error as xerrors with opaque: %+v\n", xerrors.Opaque(wrapXerrors()))

こちらを䜿うず、xerrorsを䜿った堎合でも呌び出し元のフレヌムが出なくなりたす。

Case5:

wrap error as errors: Wrap Error!! cause: Oops!!

======

wrap error as xerrors with opaque: Wrap Error!! cause: Oops!!

぀たり、errorsを䜿った時ず同じになりたしたね。ネストされた゚ラヌは保持しおいるようです。

およそこんなずころでしょうか。だいたい雰囲気はわかった気がしたす。

呌び出し元フレヌムの取埗方法

ずころで、xerrorsはどうやっお呌び出し元のフレヌムを取埗しおいるんでしょうか。

こちらのようです。

https://github.com/golang/xerrors/blob/5ec99f83aff198f5fbd629d6c8d8eb38a04218ca/frame.go#L24

runtimeパッケヌゞを䜿っおいるようですね。

runtime - The Go Programming Language

たた、他の蚀語でのコヌルスタックのようにならないのは、文字列に倉換しおいる時に1行分しか残しおいないからみたいですね。

https://github.com/golang/xerrors/blob/5ec99f83aff198f5fbd629d6c8d8eb38a04218ca/frame.go#L48

https://github.com/golang/xerrors/blob/5ec99f83aff198f5fbd629d6c8d8eb38a04218ca/frame.go#L31-L41

xerrorsが䜿っおいるのはruntime.Callersですが、

https://github.com/golang/xerrors/blob/5ec99f83aff198f5fbd629d6c8d8eb38a04218ca/frame.go#L24

たずはruntime.Callerを䜿っおみたしょう。

runtime / func Caller

    60     fmt.Println("\nCase6:\n")
    61  
    62     pc, file, line, ok := callRuntimeCaller0()
    63     fmt.Printf("%v, %s, %d, %t\n", pc, file, line, ok)
    64  
    65     printSeparator()
    66  
    67     pc, file, line, ok = callRuntimeCaller1()
    68     fmt.Printf("%v, %s, %d, %t\n", pc, file, line, ok)

それぞれ、runtime.Callerに0ず1を枡しおいたす。

   143 func callRuntimeCaller0() (uintptr, string, int, bool) {
   144     return runtime.Caller(0)
   145 }
   146  
   147 func callRuntimeCaller1() (uintptr, string, int, bool) {
   148     return runtime.Caller(1)
   149 }

実行結果。

Case6:

4856742, /path/to/main.go, 144, true

======

4857125, /path/to/main.go, 67, true

0の方は、たさにこの䜍眮を衚瀺しおいたす。

   143 func callRuntimeCaller0() (uintptr, string, int, bool) {
   144     return runtime.Caller(0)
   145 }

察しお1の方は、呌び出し元のコヌドの䜍眮を衚瀺しおいたす。

    67      pc, file, line, ok = callRuntimeCaller1()

぀たり、runtime.Callerに䞎える倀は、どれだけ呌び出し元たでのフレヌムをスキップするかを意味したす。

次にruntime.Callersを䜿っおみたす。

Packages runtime / func Callers

こちらの第1匕数は、どれだけフレヌムをスキップするかです。そしお、第2匕数にフレヌムを保存したす。

この結果ずruntime.CallersFramesを組み合わせるこずで、スタックトレヌスを再珟できたす。

Package runtime / func CallersFrames

    72     pcs := callRuntimeCallers0()
    73     frames := runtime.CallersFrames(pcs)
    74  
    75     for {
    76         frame, more := frames.Next()
    77         fmt.Printf("  %s(%s:%d)\n", frame.Function, frame.File, frame.Line)
    78  
    79         if !more {
    80             break
    81         }
    82     }
    83  
    84     printSeparator()
    85  
    86     pcs = callRuntimeCallers1()
    87     frames = runtime.CallersFrames(pcs)
    88  
    89     for {
    90         frame, more := frames.Next()
    91         fmt.Printf("  %s(%s:%d)\n", frame.Function, frame.File, frame.Line)
    92  
    93         if !more {
    94             break
    95         }
    96     }

ただ、どのくらいの数のフレヌムを取埗するかは、固定で決めないずいけなさそうですけどね。

   151 func callRuntimeCallers0() []uintptr {
   152     var pcs [3]uintptr
   153     runtime.Callers(0, pcs[:])
   154     return pcs[:]
   155 }
   156  
   157 func callRuntimeCallers1() []uintptr {
   158     var pcs [3]uintptr
   159     runtime.Callers(1, pcs[:])
   160     return pcs[:]
   161 }

結果。

  runtime.Callers(/usr/lib/go-1.16/src/runtime/extern.go:229)
  main.callRuntimeCallers0(/path/to/main.go:153)
  main.main(/path/to/main.go:72)

======

  main.callRuntimeCallers1(/path/to/main.go:159)
  main.main(/path/to/main.go:86)
  runtime.main(/usr/lib/go-1.16/src/runtime/proc.go:225)

runtime.Callersの第1匕数を0にするず、runtime.Callers自身が含たれたすね 。どこたでスキップするかを指定するのは、
重芁な様子です。

フレヌムを取埗するたでに、さらに関数呌び出しを挟んでみたしょう。

   100     pcs = wrapCallRuntimeCaller1()
   101     frames = runtime.CallersFrames(pcs)
   102  
   103     for {
   104         frame, more := frames.Next()
   105         fmt.Printf("  %s(%s:%d)\n", frame.Function, frame.File, frame.Line)
   106  
   107         if !more {
   108             break
   109         }
   110     }

こんな感じですね。

   157 func callRuntimeCallers1() []uintptr {
   158     var pcs [3]uintptr
   159     runtime.Callers(1, pcs[:])
   160     return pcs[:]
   161 }
   162  
   163 func wrapCallRuntimeCaller1() []uintptr {
   164     return callRuntimeCallers1()
   165 }

結果。それっぜくなっおいたすね。

  main.callRuntimeCallers1(/path/to/main.go:159)
  main.wrapCallRuntimeCaller1(/path/to/main.go:164)
  main.main(/path/to/main.go:100)

このあたりの雰囲気はわかった気がしたす。

ずはいえ、xerrorsの方も保持しおいるフレヌムも3぀分だけのようですし、

https://github.com/golang/xerrors/blob/5ec99f83aff198f5fbd629d6c8d8eb38a04218ca/frame.go#L16

そもそもこういうスタックトレヌスのようなものに頌るような蚀語ではないのでしょうね。

このあたりの情報は、参考皋床に芚えおおこうかなず思いたす。

オマケ

最埌に、行番号なしの゜ヌスコヌドを茉せおおきたしょう。

main.go

package main

import (
    "errors"
    "fmt"
    "runtime"

    "golang.org/x/xerrors"
)

func main() {
    fmt.Println("Case1:\n")

    fmt.Printf("New error as errors: %v\n", errors.New("Oops!!"))
    fmt.Println()
    fmt.Printf("New error as errors: %+v\n", errors.New("Oops!!"))

    printSeparator()

    fmt.Printf("New error as xerrors: %v\n", xerrors.New("Oops!!"))
    fmt.Println()
    fmt.Printf("New error as xerrors: %+v\n", xerrors.New("Oops!!"))

    fmt.Println("\nCase2:\n")

    fmt.Printf("call error as errors: %+v\n", callErrors())

    printSeparator()

    fmt.Printf("call error as xerrors: %+v\n", callXerrors())

    fmt.Println("\nCase3:\n")

    fmt.Printf("wrap error as errors: %+v\n", wrapErrors())

    printSeparator()

    fmt.Printf("wrap error as xerrors: %+v\n", wrapXerrors())

    fmt.Println("\nCase4:\n")

    fmt.Printf("unwrap as errors: %+v\n", errors.Unwrap(wrapErrors()))

    printSeparator()

    fmt.Printf("unwrap as xerrors: %+v\n", xerrors.Unwrap(wrapXerrors()))

    fmt.Println()

    fmt.Printf("unwrap as xerrors / errors.Unwrap: %+v\n", errors.Unwrap(wrapXerrors()))

    fmt.Println("\nCase5:\n")

    fmt.Printf("wrap error as errors: %+v\n", wrapErrors())

    printSeparator()

    fmt.Printf("wrap error as xerrors with opaque: %+v\n", xerrors.Opaque(wrapXerrors()))

    fmt.Println("\nCase6:\n")

    pc, file, line, ok := callRuntimeCaller0()
    fmt.Printf("%v, %s, %d, %t\n", pc, file, line, ok)

    printSeparator()

    pc, file, line, ok = callRuntimeCaller1()
    fmt.Printf("%v, %s, %d, %t\n", pc, file, line, ok)

    printSeparator()

    pcs := callRuntimeCallers0()
    frames := runtime.CallersFrames(pcs)

    for {
        frame, more := frames.Next()
        fmt.Printf("  %s(%s:%d)\n", frame.Function, frame.File, frame.Line)

        if !more {
            break
        }
    }

    printSeparator()

    pcs = callRuntimeCallers1()
    frames = runtime.CallersFrames(pcs)

    for {
        frame, more := frames.Next()
        fmt.Printf("  %s(%s:%d)\n", frame.Function, frame.File, frame.Line)

        if !more {
            break
        }
    }

    printSeparator()

    pcs = wrapCallRuntimeCaller1()
    frames = runtime.CallersFrames(pcs)

    for {
        frame, more := frames.Next()
        fmt.Printf("  %s(%s:%d)\n", frame.Function, frame.File, frame.Line)

        if !more {
            break
        }
    }
}

func printSeparator() {
    fmt.Println()
    fmt.Println("======")
    fmt.Println()
}

func causeErrorUseErrors() error {
    return errors.New("Oops!!")
}

func callErrors() error {
    return causeErrorUseErrors()
}

func wrapErrors() error {
    return fmt.Errorf("Wrap Error!! cause: %w", causeErrorUseErrors())
}

func causeErrorUseXerrors() error {
    return xerrors.New("Oops!!")
}

func callXerrors() error {
    return causeErrorUseXerrors()
}

func wrapXerrors() error {
    return xerrors.Errorf("Wrap Error!! cause: %w", causeErrorUseXerrors())
}

func callRuntimeCaller0() (uintptr, string, int, bool) {
    return runtime.Caller(0)
}

func callRuntimeCaller1() (uintptr, string, int, bool) {
    return runtime.Caller(1)
}

func callRuntimeCallers0() []uintptr {
    var pcs [3]uintptr
    runtime.Callers(0, pcs[:])
    return pcs[:]
}

func callRuntimeCallers1() []uintptr {
    var pcs [3]uintptr
    runtime.Callers(1, pcs[:])
    return pcs[:]
}

func wrapCallRuntimeCaller1() []uintptr {
    return callRuntimeCallers1()
}