使用 go 读取 ETW 提供程序
Reading ETW providers using go
我正在尝试从 Advapi32.dll 中访问 EnumerateTraceGuids 函数。
我还处于非常早期的阶段,仍在尝试破译我必须做什么。我有以下代码不断给我错误:87,意思是 ERROR_INVALID_PARAMETER.
虽然它只是写入而不是读取,但我已将此文件用作起点:
https://github.com/moby/moby/blob/master/daemon/logger/etwlogs/etwlogs_windows.go
我要调用的函数的官方文档在这里:
https://msdn.microsoft.com/en-us/library/windows/desktop/aa363713(v=vs.85).aspx
它需要 GuidPropertiesArray [in, out] 指向 TRACE_GUID_PROPERTIES 结构的指针数组。这个结构如下(https://msdn.microsoft.com/en-us/library/windows/desktop/aa364143(v=vs.85).aspx)
typedef struct _TRACE_GUID_PROPERTIES {
GUID Guid;
ULONG GuidType;
ULONG LoggerId;
ULONG EnableLevel;
ULONG EnableFlags;
BOOLEAN IsEnable;
} TRACE_GUID_PROPERTIES, *PTRACE_GUID_PROPERTIES;
我有以下代码来尝试执行此操作:
package main
import (
"errors"
"fmt"
"syscall"
"unsafe"
"github.com/sirupsen/logrus"
"golang.org/x/sys/windows"
)
const (
win32CallSuccess = 0
MaxProv = 50
nbProviders = 50
)
var (
modAdvapi32 = windows.NewLazySystemDLL("Advapi32.dll")
procEnumerateTraceGuids = modAdvapi32.NewProc("EnumerateTraceGuids")
)
type ulong int32
type TRACE_GUID_PROPERTIES struct {
Guid syscall.GUID
GuidType ulong
LoggerId ulong
EnableLevel ulong
EnableFlags ulong
IsEnable bool
}
func callEnumerateTraceGuids() error {
GuidPropertiesArray:= make([]TRACE_GUID_PROPERTIES, 1)
ptr := &GuidPropertiesArray[0]
ret, _, _ := procEnumerateTraceGuids.Call(uintptr(unsafe.Pointer(&ptr)), MaxProv, nbProviders)
if ret != win32CallSuccess {
errorMessage := fmt.Sprintf("Failed to register ETW provider. Error: %d", ret)
logrus.Error(errorMessage)
return errors.New(errorMessage)
}
return nil
}
func main() {
callEnumerateTraceGuids()
}
此时我不确定我必须做什么。我尝试了很多初始化数组的变体,但都没有成功。
希望有人能指出我正确的方向。
谢谢!
编辑:根据评论更改了代码,但仍然出现相同的错误。
PS :这是我第一次在 Whosebug 上发帖,我已经被告知我在发布问题后不到 12 小时就懒惰了(耶!)所以我不确定我在问这对...我不太熟悉 go 并且从来没有从 go 调用过 windows DLL 并且因为我一直在打那个 ERROR_INVALID_PARAMETER 我想伸出手来尝试通过这第一堵墙能够同时掌握一些概念。希望这有助于理解我的请求(即,我平安而来)。
好的,我有一点空闲时间并且可以使用 Windows XP 盒子,
所以我决定重温我的 Windows 编程技能
并编写了一个可行的解决方案:
package main
import (
"golang.org/x/sys/windows"
"log"
"syscall"
"unsafe"
)
var (
modAdvapi32 = windows.NewLazySystemDLL("advapi32")
procEnumerateTraceGuids = modAdvapi32.NewProc("EnumerateTraceGuids")
)
type traceGuidProperties struct {
guid syscall.GUID
guidType uint32
loggerId uint32
enableLevel uint32
enableFlags uint32
isEnable uint32
}
func enumerateTraceGuids(ptr **traceGuidProperties, count uint32, out *uint32) error {
rc, _, _ := procEnumerateTraceGuids.Call(uintptr(unsafe.Pointer(ptr)),
uintptr(count), uintptr(unsafe.Pointer(out)))
if rc != 0 {
return syscall.Errno(rc)
}
return nil
}
func enumTraceGuids() ([]*traceGuidProperties, error) {
var errMoreData = syscall.Errno(234)
var (
dummyProps traceGuidProperties
dummyPtr = &dummyProps
count uint32
)
err := enumerateTraceGuids(&dummyPtr, 0, &count)
if err != errMoreData {
return nil, err
}
items := make([]*traceGuidProperties, count)
for i := range items {
items[i] = new(traceGuidProperties)
}
for {
err = enumerateTraceGuids(&items[0], count, &count)
if err == nil {
break
}
if err != errMoreData {
return nil, err
}
for i := 0; i < int(count)-len(items); i++ {
items = append(items, new(traceGuidProperties))
}
}
return items[:count], nil
}
func main() {
log.SetFlags(0)
data, err := enumTraceGuids()
if err != nil {
log.Fatal(err)
}
log.Printf("len(data)=%d\n", len(data))
for i := range data {
log.Println(*(data[i]))
}
}
要点:
我跟你说错了
«你……应该分配一个结构数组(而不是指针)»——事实上
EnumerateTraceGuids
确实需要一个指针数组。
如提示here,
EnumerateTraceGuids
的工作方式有两个微妙之处:
- 与其文档所述相反,
它实际上支持用
PropertyArrayCount
调用
参数设置为 0,在这种情况下,它应该 return ERROR_MORE_DATA
同时将 GuidCount
设置为输入的元素数
(下一次)调用成功完成所需的数组。
IOW,这样我们就知道系统当前有多少跟踪 GUID
"knows about".
- 尽管如此,即使在这种情况下,该函数也会执行有效性检查
在输入数组上(见下文)。
事实证明,该函数需要一个指向的指针数组
TRACE_GUID_PROPERTIES
块 由您分配。
换句话说,如果它说你知道大约 10 个跟踪 GUID,
你必须分配 10 个 TRACE_GUID_PROPERTIES
类型的值,
然后创建一个包含 10 个指向这些值的指针的数组并传递一个指针
到该数组的第一个元素到函数。
请注意,发生的更改之间存在固有的竞争
在系统中(由于各种原因添加或删除的痕迹)
以及对 EnumerateTraceGuids
.
的调用
这意味着如果第一次调用此函数时告诉您 "knows"
大约 10 个跟踪 GUID,在下一次调用时可能会出现
已经有 20 个跟踪 GUID,或 5 个 GUID
(或任何其他数量的 FWIW)。
所以我们通过以下方式考虑这两种可能性:
首先我们调用一个指向单个(但有效)的指针
TRACE_GUID_PROPERTIES
值,静态分配
(因此函数 "sees" 看起来像单个元素的数组),
在告诉函数输入 "array" 有零个元素时。
我们预计函数会失败 ERROR_MORE_DATA
并将它 "knows" 的跟踪 GUID 的实际数量放入变量中
我们已经为它提供了一个指针。
我们分配了那么多TRACE_GUID_PROPERTIES
内存块
第一次调用时指示的功能。
为此,我们使用 new()
内置函数
就像标准 C 库中的 malloc()
——它为
指定类型的值和 returns 指向分配的指针
内存块.
我们创建一个指向这些已分配内存块的指针数组
并再次调用 EnumerateTraceGuids
。
如果成功,我们会处理它 returned less 的可能性
比我们分配的元素,并重新切片我们的切片。
如果 ERROR_MORE_DATA
失败,我们扩展切片
无论需要多少元素(为它们分配内存
TRACE_GUID_PROPERTIES
首先阻塞),然后尝试再次调用该函数。
"magic number" 234 是 ERROR_MORE_DATA
值的实际代码。
对于最初的困惑,我们深表歉意。
我正在尝试从 Advapi32.dll 中访问 EnumerateTraceGuids 函数。 我还处于非常早期的阶段,仍在尝试破译我必须做什么。我有以下代码不断给我错误:87,意思是 ERROR_INVALID_PARAMETER.
虽然它只是写入而不是读取,但我已将此文件用作起点: https://github.com/moby/moby/blob/master/daemon/logger/etwlogs/etwlogs_windows.go
我要调用的函数的官方文档在这里: https://msdn.microsoft.com/en-us/library/windows/desktop/aa363713(v=vs.85).aspx
它需要 GuidPropertiesArray [in, out] 指向 TRACE_GUID_PROPERTIES 结构的指针数组。这个结构如下(https://msdn.microsoft.com/en-us/library/windows/desktop/aa364143(v=vs.85).aspx)
typedef struct _TRACE_GUID_PROPERTIES {
GUID Guid;
ULONG GuidType;
ULONG LoggerId;
ULONG EnableLevel;
ULONG EnableFlags;
BOOLEAN IsEnable;
} TRACE_GUID_PROPERTIES, *PTRACE_GUID_PROPERTIES;
我有以下代码来尝试执行此操作:
package main
import (
"errors"
"fmt"
"syscall"
"unsafe"
"github.com/sirupsen/logrus"
"golang.org/x/sys/windows"
)
const (
win32CallSuccess = 0
MaxProv = 50
nbProviders = 50
)
var (
modAdvapi32 = windows.NewLazySystemDLL("Advapi32.dll")
procEnumerateTraceGuids = modAdvapi32.NewProc("EnumerateTraceGuids")
)
type ulong int32
type TRACE_GUID_PROPERTIES struct {
Guid syscall.GUID
GuidType ulong
LoggerId ulong
EnableLevel ulong
EnableFlags ulong
IsEnable bool
}
func callEnumerateTraceGuids() error {
GuidPropertiesArray:= make([]TRACE_GUID_PROPERTIES, 1)
ptr := &GuidPropertiesArray[0]
ret, _, _ := procEnumerateTraceGuids.Call(uintptr(unsafe.Pointer(&ptr)), MaxProv, nbProviders)
if ret != win32CallSuccess {
errorMessage := fmt.Sprintf("Failed to register ETW provider. Error: %d", ret)
logrus.Error(errorMessage)
return errors.New(errorMessage)
}
return nil
}
func main() {
callEnumerateTraceGuids()
}
此时我不确定我必须做什么。我尝试了很多初始化数组的变体,但都没有成功。 希望有人能指出我正确的方向。 谢谢!
编辑:根据评论更改了代码,但仍然出现相同的错误。
PS :这是我第一次在 Whosebug 上发帖,我已经被告知我在发布问题后不到 12 小时就懒惰了(耶!)所以我不确定我在问这对...我不太熟悉 go 并且从来没有从 go 调用过 windows DLL 并且因为我一直在打那个 ERROR_INVALID_PARAMETER 我想伸出手来尝试通过这第一堵墙能够同时掌握一些概念。希望这有助于理解我的请求(即,我平安而来)。
好的,我有一点空闲时间并且可以使用 Windows XP 盒子, 所以我决定重温我的 Windows 编程技能 并编写了一个可行的解决方案:
package main
import (
"golang.org/x/sys/windows"
"log"
"syscall"
"unsafe"
)
var (
modAdvapi32 = windows.NewLazySystemDLL("advapi32")
procEnumerateTraceGuids = modAdvapi32.NewProc("EnumerateTraceGuids")
)
type traceGuidProperties struct {
guid syscall.GUID
guidType uint32
loggerId uint32
enableLevel uint32
enableFlags uint32
isEnable uint32
}
func enumerateTraceGuids(ptr **traceGuidProperties, count uint32, out *uint32) error {
rc, _, _ := procEnumerateTraceGuids.Call(uintptr(unsafe.Pointer(ptr)),
uintptr(count), uintptr(unsafe.Pointer(out)))
if rc != 0 {
return syscall.Errno(rc)
}
return nil
}
func enumTraceGuids() ([]*traceGuidProperties, error) {
var errMoreData = syscall.Errno(234)
var (
dummyProps traceGuidProperties
dummyPtr = &dummyProps
count uint32
)
err := enumerateTraceGuids(&dummyPtr, 0, &count)
if err != errMoreData {
return nil, err
}
items := make([]*traceGuidProperties, count)
for i := range items {
items[i] = new(traceGuidProperties)
}
for {
err = enumerateTraceGuids(&items[0], count, &count)
if err == nil {
break
}
if err != errMoreData {
return nil, err
}
for i := 0; i < int(count)-len(items); i++ {
items = append(items, new(traceGuidProperties))
}
}
return items[:count], nil
}
func main() {
log.SetFlags(0)
data, err := enumTraceGuids()
if err != nil {
log.Fatal(err)
}
log.Printf("len(data)=%d\n", len(data))
for i := range data {
log.Println(*(data[i]))
}
}
要点:
我跟你说错了 «你……应该分配一个结构数组(而不是指针)»——事实上
EnumerateTraceGuids
确实需要一个指针数组。如提示here,
EnumerateTraceGuids
的工作方式有两个微妙之处:- 与其文档所述相反,
它实际上支持用
PropertyArrayCount
调用 参数设置为 0,在这种情况下,它应该 returnERROR_MORE_DATA
同时将GuidCount
设置为输入的元素数 (下一次)调用成功完成所需的数组。 IOW,这样我们就知道系统当前有多少跟踪 GUID "knows about". - 尽管如此,即使在这种情况下,该函数也会执行有效性检查 在输入数组上(见下文)。
- 与其文档所述相反,
它实际上支持用
事实证明,该函数需要一个指向的指针数组
TRACE_GUID_PROPERTIES
块 由您分配。换句话说,如果它说你知道大约 10 个跟踪 GUID, 你必须分配 10 个
TRACE_GUID_PROPERTIES
类型的值, 然后创建一个包含 10 个指向这些值的指针的数组并传递一个指针 到该数组的第一个元素到函数。请注意,发生的更改之间存在固有的竞争 在系统中(由于各种原因添加或删除的痕迹) 以及对
的调用EnumerateTraceGuids
.这意味着如果第一次调用此函数时告诉您 "knows" 大约 10 个跟踪 GUID,在下一次调用时可能会出现 已经有 20 个跟踪 GUID,或 5 个 GUID (或任何其他数量的 FWIW)。
所以我们通过以下方式考虑这两种可能性:
首先我们调用一个指向单个(但有效)的指针
TRACE_GUID_PROPERTIES
值,静态分配 (因此函数 "sees" 看起来像单个元素的数组), 在告诉函数输入 "array" 有零个元素时。我们预计函数会失败
ERROR_MORE_DATA
并将它 "knows" 的跟踪 GUID 的实际数量放入变量中 我们已经为它提供了一个指针。我们分配了那么多
TRACE_GUID_PROPERTIES
内存块 第一次调用时指示的功能。 为此,我们使用new()
内置函数 就像标准 C 库中的malloc()
——它为 指定类型的值和 returns 指向分配的指针 内存块.我们创建一个指向这些已分配内存块的指针数组 并再次调用
EnumerateTraceGuids
。如果成功,我们会处理它 returned less 的可能性 比我们分配的元素,并重新切片我们的切片。
如果
ERROR_MORE_DATA
失败,我们扩展切片 无论需要多少元素(为它们分配内存TRACE_GUID_PROPERTIES
首先阻塞),然后尝试再次调用该函数。
"magic number" 234 是
ERROR_MORE_DATA
值的实际代码。
对于最初的困惑,我们深表歉意。