Add ioctl sniffing tool to run GPU workloads and report unsupported ioctl calls.

PiperOrigin-RevId: 644197930
This commit is contained in:
Anthony Cui
2024-06-17 18:32:27 -07:00
committed by gVisor bot
parent 80a501d8cc
commit e8ca88e167
14 changed files with 756 additions and 2 deletions
+4 -1
View File
@@ -16,6 +16,9 @@ go_library(
"uvm.go",
],
marshal = True,
visibility = ["//pkg/sentry:internal"],
visibility = [
"//pkg/sentry:internal",
"//tools:__subpackages__",
],
deps = ["//pkg/marshal"],
)
+11
View File
@@ -170,6 +170,7 @@ type NVOS00Parameters struct {
// RmAllocParamType should be implemented by all possible parameter types for
// NV_ESC_RM_ALLOC.
type RmAllocParamType interface {
GetHClass() ClassID
GetPAllocParms() P64
GetPRightsRequested() P64
SetPAllocParms(p P64)
@@ -203,6 +204,11 @@ type NVOS21Parameters struct {
Status uint32
}
// GetHClass implements RmAllocParamType.GetHClass.
func (n *NVOS21Parameters) GetHClass() ClassID {
return n.HClass
}
// GetPAllocParms implements RmAllocParamType.GetPAllocParms.
func (n *NVOS21Parameters) GetPAllocParms() P64 {
return n.PAllocParms
@@ -403,6 +409,11 @@ type NVOS64Parameters struct {
_ uint32
}
// GetHClass implements RmAllocParamType.GetHClass.
func (n *NVOS64Parameters) GetHClass() ClassID {
return n.HClass
}
// GetPAllocParms implements RmAllocParamType.GetPAllocParms.
func (n *NVOS64Parameters) GetPAllocParms() P64 {
return n.PAllocParms
+1 -1
View File
@@ -54,7 +54,7 @@ go_library(
],
visibility = [
"//pkg/sentry:internal",
"//tools/gpu:__subpackages__",
"//tools:__subpackages__",
],
deps = [
"//pkg/abi/linux",
+39
View File
@@ -0,0 +1,39 @@
load("//tools:defs.bzl", "cc_binary", "go_binary", "proto_library")
package(
default_applicable_licenses = ["//:license"],
licenses = ["notice"],
)
proto_library(
name = "ioctl",
srcs = ["ioctl.proto"],
visibility = ["//tools/ioctl_sniffer:__subpackages__"],
)
cc_binary(
name = "ioctl_hook",
srcs = [
"ioctl_hook.cc",
"ioctl_hook.h",
"sniffer_bridge.cc",
"sniffer_bridge.h",
],
linkshared = True,
deps = [
":ioctl_cc_proto",
"@com_google_absl//absl/strings",
"@com_google_absl//absl/strings:str_format",
"@com_google_protobuf//src/google/protobuf/io",
],
)
go_binary(
name = "run_sniffer",
srcs = ["run_sniffer.go"],
static = True,
deps = [
"//pkg/log",
"//tools/ioctl_sniffer/sniffer",
],
)
+52
View File
@@ -0,0 +1,52 @@
# Ioctl Sniffer
This tool provides a way to profile GPU workloads, sniff out all the Nvidia
`ioctl(2)` calls that are involved, and ultimately filter out the calls that are
currently unsupported by nvproxy.
This is accomplished by providing a `libioctl_hook.so` shared library, which can
be `LD_PRELOAD`ed and intercepts all `ioctl(2)` calls made. Any calls made to
known Nvidia device files are then captured and parsed by the `sniffer` Go
package. The sniffer compares against nvproxy's list of supported `ioctl(2)`
numbers for the current driver version, and checks if the given call is
contained in the list. For `NV_ESC_RM_CONTROL` and `NV_ESC_RM_ALLOC` calls, it
also extracts the control command and allocation class respectively, and checks
if nvproxy supports them.
## Usage
To start, we need to build the shared library and Go binary:
```
make copy TARGETS=//tools/ioctl_sniffer:run_sniffer DESTINATION=bin/
make copy TARGETS=//tools/ioctl_sniffer:ioctl_hook DESTINATION=bin/
```
Once we have the binary, we can hook into any GPU workload by passing the
corresponding command to run it to `run_sniffer`, like so:
```
./run_sniffer nvidia-smi
```
This should run the workload as normal and provide an output of all the
unsupported `ioctl(2)` calls it detected at the end:
```
============== Unsupported ioctls ==============
Frontend:
UVM:
Control:
Control ioctl: request=0xc020462a [nr=0x2a (42), cmd=0x20810110 (545325328)] => ret=0
Control ioctl: request=0xc020462a [nr=0x2a (42), cmd=0x2080014b (545259851)] => ret=0
...
Alloc:
Alloc ioctl: request=0xc030462b [nr=0x2b (43), hClass=0xc639 (50745)] => ret=0
Alloc ioctl: request=0xc030462b [nr=0x2b (43), hClass=0xc640 (50752)] => ret=0
...
Unknown:
```
Note that by default, `run_sniffer` assumes the shared library is located in the
same directory. You can specify the path to the library with the optional
`-ld_preload` flag.
+32
View File
@@ -0,0 +1,32 @@
// Copyright 2024 The gVisor Authors.
//
// Licensed 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.
syntax = "proto3";
package gvisor;
message Ioctl {
// The path of the file that `fd` is pointing to.
string fd_path = 1;
// The request argument of the ioctl.
uint64 request = 2;
// The return value of the ioctl.
int32 ret = 3;
// The data pointed to by `argp`. For UVM ioctl calls, the argument size is
// not easily accessible, so `arg_data` will be empty in this case.
bytes arg_data = 4;
}
+90
View File
@@ -0,0 +1,90 @@
// Copyright 2024 The gVisor Authors.
//
// Licensed 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.
#define _GNU_SOURCE 1 // Needed for access to RTLD_NEXT
#include "tools/ioctl_sniffer/ioctl_hook.h"
#include <asm/ioctl.h>
#include <dlfcn.h>
#include <limits.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <cstring>
#include <string>
#include "absl/strings/match.h"
#include "absl/strings/str_cat.h"
#include "tools/ioctl_sniffer/ioctl.pb.h"
#include "tools/ioctl_sniffer/sniffer_bridge.h"
using gvisor::Ioctl;
libc_ioctl libc_ioctl_handle = nullptr;
void init_libc_ioctl_handle() {
if (libc_ioctl_handle) {
return;
}
libc_ioctl_handle = (libc_ioctl)dlsym(RTLD_NEXT, "ioctl");
if (!libc_ioctl_handle) {
printf("Failed to hook ioctl: %s\n", dlerror());
exit(1);
}
}
extern "C" {
int ioctl(int fd, uint64_t request, void *argp) {
if (!libc_ioctl_handle) {
init_libc_ioctl_handle();
}
// Forward the ioctl call.
int ret = libc_ioctl_handle(fd, request, argp);
// Check the file name to see if this is an Nvidia ioctl.
// We only want to do protobuf logging for these ioctls.
char file_name[PATH_MAX + 1];
int n = readlink(absl::StrCat("/proc/self/fd/", fd).c_str(), file_name,
sizeof(file_name) - 1);
if (n < 0) {
return ret;
}
file_name[n] = '\0';
if (!absl::StartsWith(file_name, "/dev/nvidia")) {
return ret;
}
// Prepare ioctl proto for logging.
Ioctl info;
info.set_fd_path(file_name);
info.set_request(request);
info.set_ret(ret);
// ioctl calls to uvm don't encode their size in the request.
uint32_t arg_size =
strcmp(file_name, "/dev/nvidia-uvm") == 0 ? 0 : _IOC_SIZE(request);
info.set_arg_data(argp, arg_size);
WriteIoctlProto(info);
return ret;
}
} // extern "C"
+24
View File
@@ -0,0 +1,24 @@
// Copyright 2024 The gVisor Authors.
//
// Licensed 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.
#ifndef TOOLS_IOCTL_SNIFFER_IOCTL_HOOK_H_
#define TOOLS_IOCTL_SNIFFER_IOCTL_HOOK_H_
#include <stdint.h>
typedef int (*libc_ioctl)(int fd, uint64_t request, void *argp);
void init_libc_ioctl_handle();
#endif // TOOLS_IOCTL_SNIFFER_IOCTL_HOOK_H_
+79
View File
@@ -0,0 +1,79 @@
// Copyright 2024 The gVisor Authors.
//
// Licensed 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 main sets up the ioctl sniffer and runs a given command.
package main
import (
"flag"
"fmt"
"os"
"os/exec"
"gvisor.dev/gvisor/pkg/log"
"gvisor.dev/gvisor/tools/ioctl_sniffer/sniffer"
)
var ldPreloadPath = flag.String("ld_preload", "./libioctl_hook.so", "The path to the LD_PRELOAD library.")
// Main is our main function.
func Main() error {
flag.Parse()
if len(flag.Args()) == 0 {
return fmt.Errorf("no command specified")
}
// Init our sniffer
if err := sniffer.Init(); err != nil {
return fmt.Errorf("failed to init sniffer: %w", err)
}
// Create a pipe to read the output of the command.
r, w, err := os.Pipe()
if err != nil {
return fmt.Errorf("failed to create pipe: %w", err)
}
// Set up command from flags
cmd := exec.Command(flag.Arg(0), flag.Args()[1:]...)
cmd.ExtraFiles = []*os.File{w}
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
cmd.Env = append(os.Environ(), fmt.Sprintf("LD_PRELOAD=%s", *ldPreloadPath))
// Run the command and start reading the output.
if err := cmd.Start(); err != nil {
return fmt.Errorf("failed to run command: %w", err)
}
w.Close()
results := sniffer.ReadHookOutput(r)
// Once we've read all the output, print the list of missing ioctls.
log.Infof("%s", results)
if err := cmd.Wait(); err != nil {
return fmt.Errorf("command exited with error: %w", err)
}
return nil
}
func main() {
if err := Main(); err != nil {
log.Warningf("%v", err)
os.Exit(1)
}
}
+23
View File
@@ -0,0 +1,23 @@
load("//tools:defs.bzl", "go_library")
package(
default_applicable_licenses = ["//:license"],
licenses = ["notice"],
)
go_library(
name = "sniffer",
srcs = [
"sniffer.go",
"sniffer_bridge.go",
],
visibility = ["//tools/ioctl_sniffer:__subpackages__"],
deps = [
"//pkg/abi/linux",
"//pkg/abi/nvgpu",
"//pkg/log",
"//pkg/sentry/devices/nvproxy",
"//tools/ioctl_sniffer:ioctl_go_proto",
"@org_golang_google_protobuf//proto:go_default_library",
],
)
+278
View File
@@ -0,0 +1,278 @@
// Copyright 2024 The gVisor Authors.
//
// Licensed 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 sniffer parses the output of the ioctl hook.
package sniffer
import (
"errors"
"fmt"
"io"
"regexp"
"strings"
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/abi/nvgpu"
"gvisor.dev/gvisor/pkg/log"
"gvisor.dev/gvisor/pkg/sentry/devices/nvproxy"
pb "gvisor.dev/gvisor/tools/ioctl_sniffer/ioctl_go_proto"
)
var (
uvmDevPath = "/dev/nvidia-uvm"
ctlDevPath = "/dev/nvidiactl"
deviceDevPath = regexp.MustCompile(`/dev/nvidia(\d+)`)
)
const (
frontend = iota
uvm
control
alloc
unknown
)
// ioctlClass is the class of the ioctl as defined above.
type ioctlClass uint32
// ioctlNr is the command number of the ioctl.
type ioctlNr uint32
// controlCommand is the control command, specifically for the NV_ESC_RM_CONTROL ioctl.
type controlCommand uint32
// allocClass is the alloc class, specifically for the NV_ESC_RM_ALLOC ioctl.
type allocClass uint32
func (c ioctlClass) String() string {
switch c {
case frontend:
return "Frontend"
case uvm:
return "UVM"
case control:
return "Control"
case alloc:
return "Alloc"
default:
return "Unknown"
}
}
var (
suppFrontendIoctls, suppUvmIoctls, suppControlCmds, suppAllocClasses map[uint32]struct{}
)
// Ioctl contains the parsed ioctl protobuf information.
type Ioctl struct {
pb *pb.Ioctl
class ioctlClass
nr ioctlNr
cmd controlCommand // Control ioctls only.
hClass allocClass // Alloc ioctls only.
}
// IsSupported returns true if the ioctl is supported by nvproxy.
func (i Ioctl) IsSupported() bool {
switch i.class {
case frontend:
_, ok := suppFrontendIoctls[uint32(i.nr)]
return ok
case uvm:
_, ok := suppUvmIoctls[uint32(i.nr)]
return ok
case control:
_, ok := suppControlCmds[uint32(i.cmd)]
return ok
case alloc:
_, ok := suppAllocClasses[uint32(i.hClass)]
return ok
default:
return false
}
}
func (i Ioctl) String() string {
switch i.class {
case control:
return fmt.Sprintf("%s ioctl: request=%#x [nr=%#x (%d), cmd=%#x (%d)] => ret=%d",
i.class, i.pb.GetRequest(), i.nr, i.nr, i.cmd, i.cmd, i.pb.GetRet())
case alloc:
return fmt.Sprintf("%s ioctl: request=%#x [nr=%#x (%d), hClass=%#x (%d)] => ret=%d",
i.class, i.pb.GetRequest(), i.nr, i.nr, i.hClass, i.hClass, i.pb.GetRet())
default:
return fmt.Sprintf("%s ioctl: request=%#x [nr=%#x (%d), size=%d] => ret=%d",
i.class, i.pb.GetRequest(), i.nr, i.nr, len(i.pb.GetArgData()), i.pb.GetRet())
}
}
// Results contains the list of unsupported ioctls.
type Results struct {
unsupportedControl map[controlCommand]Ioctl
unsupportedAlloc map[allocClass]Ioctl
unsupportedOther map[ioctlClass]map[ioctlNr]Ioctl
}
// NewResults creates a new Results object.
func NewResults() *Results {
return &Results{
unsupportedControl: make(map[controlCommand]Ioctl),
unsupportedAlloc: make(map[allocClass]Ioctl),
unsupportedOther: make(map[ioctlClass]map[ioctlNr]Ioctl),
}
}
// AddUnsupportedIoctl adds an unsupported ioctl to the results.
func (r *Results) AddUnsupportedIoctl(ioctl Ioctl) {
switch ioctl.class {
case control:
r.unsupportedControl[ioctl.cmd] = ioctl
case alloc:
r.unsupportedAlloc[ioctl.hClass] = ioctl
default:
if r.unsupportedOther[ioctl.class] == nil {
r.unsupportedOther[ioctl.class] = make(map[ioctlNr]Ioctl)
}
r.unsupportedOther[ioctl.class][ioctl.nr] = ioctl
}
}
func printIoctls[T comparable](b *strings.Builder, class ioctlClass, m map[T]Ioctl) {
if len(m) == 0 {
fmt.Fprintf(b, "%v: None\n", class)
return
}
fmt.Fprintf(b, "%v:\n", class)
for _, ioctl := range m {
fmt.Fprintf(b, "\t%v\n", ioctl)
}
}
func (r *Results) String() string {
// We will rarely print out the results, so allocating a new strings.Builder
// each time is fine.
b := new(strings.Builder)
fmt.Fprintln(b, "============== Unsupported ioctls ==============")
printIoctls(b, frontend, r.unsupportedOther[frontend])
printIoctls(b, uvm, r.unsupportedOther[uvm])
printIoctls(b, control, r.unsupportedControl)
printIoctls(b, alloc, r.unsupportedAlloc)
printIoctls(b, unknown, r.unsupportedOther[unknown])
return b.String()
}
// Init reads from nvproxy and sets up the supported ioctl maps.
func Init() error {
nvproxy.Init()
// Load the ABI for the host driver.
driverVerStr, err := nvproxy.HostDriverVersion()
if err != nil {
return fmt.Errorf("failed to get host driver version: %w", err)
}
driverVer, err := nvproxy.DriverVersionFrom(driverVerStr)
if err != nil {
return fmt.Errorf("failed to parse host driver version: %w", err)
}
log.Infof("Host driver version: %v", driverVer)
var ok bool
suppFrontendIoctls, suppUvmIoctls, suppControlCmds, suppAllocClasses, ok = nvproxy.SupportedIoctls(driverVer)
if !ok {
return fmt.Errorf("host driver version %s is not supported", driverVer)
}
return nil
}
// ReadHookOutput reads the output of the ioctl hook until an EOF is reached.
func ReadHookOutput(r io.Reader) *Results {
res := NewResults()
for {
ioctlPB, err := ReadIoctlProto(r)
if err != nil {
if !errors.Is(err, io.EOF) {
log.Warningf("Error reading ioctl proto: %v", err)
}
break
}
// Parse the protobuf
ioctl, err := ParseIoctlOutput(ioctlPB)
if err != nil {
log.Warningf("Error parsing ioctl %v: %v", ioctlPB, err)
continue
}
log.Debugf("%s", ioctl)
if !ioctl.IsSupported() {
res.AddUnsupportedIoctl(ioctl)
}
}
return res
}
// ParseIoctlOutput parses an ioctl protobuf from the ioctl hook.
func ParseIoctlOutput(ioctl *pb.Ioctl) (Ioctl, error) {
parsedIoctl := Ioctl{pb: ioctl}
// Categorize and do class-specific parsing.
switch {
case ioctl.GetFdPath() == uvmDevPath:
parsedIoctl.class = uvm
parsedIoctl.nr = ioctlNr(ioctl.GetRequest())
case ioctl.GetFdPath() == ctlDevPath || deviceDevPath.MatchString(ioctl.GetFdPath()):
parsedIoctl.nr = ioctlNr(linux.IOC_NR(uint32(ioctl.GetRequest())))
switch parsedIoctl.nr {
case nvgpu.NV_ESC_RM_CONTROL:
data := ioctl.GetArgData()
if uint32(len(data)) != nvgpu.SizeofNVOS54Parameters {
return parsedIoctl, fmt.Errorf("unexpected number of bytes")
}
var ioctlParams nvgpu.NVOS54Parameters
ioctlParams.UnmarshalBytes(data)
parsedIoctl.class = control
parsedIoctl.cmd = controlCommand(ioctlParams.Cmd)
case nvgpu.NV_ESC_RM_ALLOC:
data := ioctl.GetArgData()
var isNVOS64 bool
switch uint32(len(data)) {
case nvgpu.SizeofNVOS21Parameters:
case nvgpu.SizeofNVOS64Parameters:
isNVOS64 = true
default:
return parsedIoctl, fmt.Errorf("unexpected number of bytes")
}
ioctlParams := nvgpu.GetRmAllocParamObj(isNVOS64)
ioctlParams.UnmarshalBytes(data)
parsedIoctl.class = alloc
parsedIoctl.hClass = allocClass(ioctlParams.GetHClass())
default:
parsedIoctl.class = frontend
}
default:
parsedIoctl.class = unknown
parsedIoctl.nr = ioctlNr(linux.IOC_NR(uint32(ioctl.GetRequest())))
}
return parsedIoctl, nil
}
@@ -0,0 +1,60 @@
// Copyright 2024 The gVisor Authors.
//
// Licensed 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 sniffer
import (
"encoding/binary"
"fmt"
"io"
"google.golang.org/protobuf/proto"
pb "gvisor.dev/gvisor/tools/ioctl_sniffer/ioctl_go_proto"
)
var (
protoBytesBuf []byte
)
// ReadIoctlProto reads a single ioctl proto from the given reader. Our format is:
// - 8 byte little endian uint64 containing the size of the proto.
// - The proto bytes.
//
// This should match the format in sniffer_bridge.h.
func ReadIoctlProto(r io.Reader) (*pb.Ioctl, error) {
// Read next proto from pipe.
var protoSizeBuf [8]byte
if _, err := io.ReadFull(r, protoSizeBuf[:]); err != nil {
return nil, fmt.Errorf("failed to read proto size: %w", err)
}
protoSize := binary.LittleEndian.Uint64(protoSizeBuf[:])
// See if we need to reallocate the buffer.
if cap(protoBytesBuf) < int(protoSize) {
protoBytesBuf = make([]byte, protoSize)
} else {
protoBytesBuf = protoBytesBuf[:protoSize]
}
if _, err := io.ReadFull(r, protoBytesBuf); err != nil {
return nil, fmt.Errorf("failed to read proto data: %w", err)
}
// Unmarshal and parse proto.
ioctl := &pb.Ioctl{}
if err := proto.Unmarshal(protoBytesBuf, ioctl); err != nil {
return nil, fmt.Errorf("failed to unmarshal proto: %w", err)
}
return ioctl, nil
}
+33
View File
@@ -0,0 +1,33 @@
// Copyright 2024 The gVisor Authors.
//
// Licensed 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.
#include "tools/ioctl_sniffer/sniffer_bridge.h"
#include <stdint.h>
#include <stdio.h>
#include <unistd.h>
#include "tools/ioctl_sniffer/ioctl.pb.h"
#include "google/protobuf/io/zero_copy_stream_impl.h"
void WriteIoctlProto(gvisor::Ioctl &ioctl) {
// Write size of the proto message.
uint64_t size = ioctl.ByteSizeLong();
write(LOG_OUTPUT_FD, &size, sizeof(size));
// Write the proto message.
google::protobuf::io::FileOutputStream os(LOG_OUTPUT_FD);
ioctl.SerializeToZeroCopyStream(&os);
os.Flush();
}
+30
View File
@@ -0,0 +1,30 @@
// Copyright 2024 The gVisor Authors.
//
// Licensed 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.
#ifndef TOOLS_IOCTL_SNIFFER_SNIFFER_BRIDGE_H_
#define TOOLS_IOCTL_SNIFFER_SNIFFER_BRIDGE_H_
#include "tools/ioctl_sniffer/ioctl.pb.h"
// The file descriptor to write the ioctl data to. Go's os.exec will
// always make this available.
constexpr int LOG_OUTPUT_FD = 3;
// Write the ioctl proto to the log output file descriptor. Our format is:
// - 8 byte little endian uint64 containing the size of the proto.
// - The proto bytes.
// This should match the format in sniffer_bridge.go.
void WriteIoctlProto(gvisor::Ioctl &ioctl);
#endif // TOOLS_IOCTL_SNIFFER_SNIFFER_BRIDGE_H_