blob: 5fcfd6f67f5e89929b688910945ee8266dabc66d [file] [log] [blame]
// Copyright 2021 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// This file implements a Go equivalent of:
// https://fuchsia.googlesource.com/fuchsia/+/HEAD/zircon/system/ulib/trace-provider/provider_impl.h
// https://fuchsia.googlesource.com/fuchsia/+/HEAD/zircon/system/ulib/trace-provider/provider_impl.cc
//go:build tracing
// +build tracing
// Package provider implements a trace provider service. The trace
// manager connects to trace providers to collect trace records from
// running programs.
package provider
import (
"bytes"
"context"
"fmt"
"syscall/zx"
"syscall/zx/fdio"
"syscall/zx/fidl"
"go.fuchsia.dev/fuchsia/src/connectivity/network/netstack/tracing/trace"
"go.fuchsia.dev/fuchsia/src/lib/component"
syslog "go.fuchsia.dev/fuchsia/src/lib/syslog/go"
fidlprovider "fidl/fuchsia/tracing/provider"
)
const tag = "trace-provider"
const registryServicePath = "/svc/fuchsia.tracing.provider.Registry"
var _ fidlprovider.ProviderWithCtx = (*providerImpl)(nil)
type providerImpl struct {
session *session
}
func (p *providerImpl) Initialize(_ fidl.Context, config fidlprovider.ProviderConfig) error {
if err := p.session.initializeEngine(config.BufferingMode, config.Buffer, config.Fifo, config.Categories); err != nil {
_ = syslog.ErrorTf(tag, "Initialize failed: %s", err)
}
return nil
}
func (p *providerImpl) Start(_ fidl.Context, options fidlprovider.StartOptions) error {
if err := p.session.startEngine(options.BufferDisposition, options.AdditionalCategories); err != nil {
_ = syslog.ErrorTf(tag, "Start failed: %s", err)
}
return nil
}
func (p *providerImpl) Stop(fidl.Context) error {
if err := p.session.stopEngine(); err != nil {
_ = syslog.ErrorTf(tag, "Stop failed: %s", err)
}
return nil
}
func (p *providerImpl) Terminate(fidl.Context) error {
if err := p.session.terminateEngine(); err != nil {
_ = syslog.ErrorTf(tag, "Terminate failed: %s", err)
}
return nil
}
func Create() error {
registryReq, registryInterface, err := fidlprovider.NewRegistryWithCtxInterfaceRequest()
if err != nil {
return fmt.Errorf("failed to make a registry request/interface channel: %w", err)
}
if err := fdio.ServiceConnect(registryServicePath, zx.Handle(registryReq.Channel)); err != nil {
return fmt.Errorf("failed to connect to %s: %w", registryServicePath, err)
}
providerReq, providerInterface, err := fidlprovider.NewProviderWithCtxInterfaceRequest()
if err != nil {
return fmt.Errorf("failed to make a provider request/interface channel: %w", err)
}
processName, err := getCurrentProcessName()
if err != nil {
return fmt.Errorf("failed to get the current process name: %w", err)
}
status, started, err := registryInterface.RegisterProviderSynchronously(context.Background(), *providerInterface, trace.GetCurrentProcessKoid(), processName)
if err != nil {
return fmt.Errorf("failed to register this provider interface: %w", err)
}
if zx.Status(status) != zx.ErrOk {
return &zx.Error{
Status: zx.Status(status),
Text: "fuchsia.tracing.provider/RegisterProviderSynchronously",
}
}
if started {
_ = syslog.InfoTf(tag, "tracing has already started. we won't start recording until the next Start()")
}
stub := fidlprovider.ProviderWithCtxStub{Impl: &providerImpl{session: newSession()}}
go component.Serve(context.Background(), &stub, providerReq.Channel, component.ServeOptions{
OnError: func(err error) {
_ = syslog.WarnTf(tag, "%s", err)
},
})
_ = syslog.InfoTf(tag, "Provider Created")
return nil
}
func getCurrentProcessName() (string, error) {
name := make([]byte, zx.ZX_MAX_NAME_LEN)
if err := zx.ProcHandle.GetProperty(zx.PropName, name); err != nil {
return "", err
}
n := bytes.IndexByte(name, 0)
if n == -1 {
n = len(name)
}
return string(name[:n]), nil
}