iaevents

package module
v1.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 24, 2023 License: Apache-2.0 Imports: 14 Imported by: 2

README

iaevents

iaevents is a golang library that makes accessing the Linux kernel's perf interface easier. It provides tools to load, parse and read Intel CPU events specified in JSON format available from https://github.com/intel/perfmon. It is based on Andi Kleen's pmu-tools/jevents library that provides such functionality in C language .

Library exports main features as implementation of interfaces:

  • Reader - provide events definition from JSON file(s)
  • Matcher - match selected events for Transformer
  • Transformer - transform event from event definition to proper perf event attributes
  • Activator - set up event and start measuring
  • MultiActivator - set up multi PMU event (for uncore events) and start measuring on all units
  • ActiveEventMonitor - read current value of perf event
  • EventGroupMonitor - read current values for perf events aggregated in one group
  • PlacementFactory - provide valid placement provider for selected unit and CPUs
  • PlacementProvider - provide placement of particular event

Library contains default implementation for all the listed interfaces. It handles both core and uncore type events. To transform the event from JSON definition to corresponding perf attributes the library read the perf specific data from sysfs files. The measurements are obtained with the use of perf_event_open syscall.

System requirements

The iaevents library is intended to be used only on linux 64-bit systems. To make system wide measurements for all processes/threads with "PID = -1" the process using the library requires CAP_SYS_ADMIN capability or /proc/sys/kernel/perf_event_paranoid has to be set to value less than 1.

Note: Library access all the required files in read mode only. According to principle of least privilege the user should restrict the files to read-only mode.

Note on number of open file descriptors

The library opens file descriptors whose quantity depends on number of monitored CPUs and number of monitored counters. It can easily exceed the default per process limit of allowed file descriptors. Depending on configuration, it might be required to increase the limit on the number of open file descriptors allowed. This can be done for example by using ulimit -n command.

Gather JSON files

User has to acquire json events file for specific system architecture. It can be done automatically by using the pmu-tools event_download.py script, or files can be obtained directly from this link: https://github.com/intel/perfmon

The library supports two JSON file formats: https://github.com/intel/perfmon/issues/22

Create events reader

The library requires to provide event definitions for Transfomer. It is fulfilled by Reader interface. To instantiate the default Reader implementation create new JSONFilesReader with NewFilesReader method.

reader := iaevents.NewFilesReader()

Next run AddFiles method with a path(s) to desired JSON file(s). If at least one file is in the old format, the method will add all paths, but will return DeprecatedFormatError error

err := reader.AddFiles(pathToFile1, pathToFile2)

It's possbile to get headers info from perfmon events (only for the new JSON perfmon format). GetHeaders() will return map[string]JSONHeader object, where the key of the map is path to the file

events, err := iaevents.Read()
if err != nil {
  return
}
headers := iavents.GetHeaders()
for path, header := ranger headers {
  fmt.Println(path)
  fmt.Println(header)
}

It is possible to look for default JSON file names for local CPU in provided path.

cpuid, err := iaevents.CheckCPUid()
err = iaevents.AddDefaultFiles(reader, pathToDir, cpuid)

Transform selected event(s) into perf attributes

To select the events to transform, user must use Matcher interface. The iaevents provide default matcher that matches the events by names.

matcher := iaevents.NewNameMatcher(EventName1, EventName2, MoreEventNames...)

To match all events provided by Reader, create new matcher without arguments.

matcher := iaevents.NewNameMatcher()

To resolve perf attributes of selected events call Transform method from PerfTransfomer. It returns slice of events with resolved perf attributes.

transformer := iaevents.NewPerfTransformer()
perfEvents, err := transformer.Transform(reader, matcher)

Activate events

To start measuring on PMU counters perf events need to be activated. During activation library makes call to perf_event_open syscall and obtain specific file descriptors for given event and cpu(s). The required function to activate the event is different for core and uncore events. To check if event belongs to uncore category user can check Uncore flag for perf event.

if perfEvent.Uncore {
    // Handle uncore event
}

Target process and extra perf options

TargetProcess can be used to monitor event for selected process or cgroup. To enable system wide monitoring of event use -1 as PID.

process := iaevents.NewEventTargetProcess(-1, 0)

Options gives user the possibility to modify the event with custom modifiers and perf attributes. The iaevents library provides builder to simplify creation of Options object.

builder := iaevents.NewOptions()

Use SetEnableOnExec to set perf event attribute bits with PerfBitDisabled and PerfBitEnableOnExec.

builder.SetEnableOnExec(true)

Use SetAttrModifiers to set list of custom perf event attribute modifiers. More info can be found on Perf Wiki.

builder.SetAttrModifiers(modifiers)

Build finds the correct setting for perf attributes and returns options to use with Activate methods.

options, err := builder.Build()

More about perf event attributes on linux MAN page.

Activate core event

To activate core event user need to provide on which CPU cores the event should be measured. For example to create placement providers for cores 0, 2 and 4 use NewCorePlacements function.

placements, err := iaevents.NewCorePlacements(perfEvent, 0, 2, 4)

The Activate method starts the underlying PMU counter and returns ActiveEvent for single placement.

activeEvent, err := perfEvent.Activate(placement, process, options)
Activate uncore event

Single uncore event can use several counters for every socket. The example are SBO events.

/sys/devices/uncore_sbox_0
/sys/devices/uncore_sbox_1
/sys/devices/uncore_sbox_2
/sys/devices/uncore_sbox_3

To activate uncore event a special ActivateMulti method should be used. To get all placements for event on given socket use NewUncoreAllPlacements function. The below example shows how to start uncore event on socket 1.

placements, err := iaevents.NewUncoreAllPlacements(perfEvent, 1)
activeMultiEvent, err := perfEvent.ActivateMulti(placements, process, options)
Events grouping

An event group has one event which is the group leader. The leader is created first, and the rest of the group members are created with subsequent perf_event_open() calls with group_fd being set to the file descriptor of the group leader. An event group is scheduled onto the CPU as a unit: it will be put onto the CPU only if all of the events in the group can be put onto the CPU. This means that the values of the member events can be meaningfully compared (added, divided (to get ratios), and so on) with each other, since they have counted events for the same set of executed instructions. To activate set of events in single group user needs to use ActivateGroup function on selected perf events.

activeEventGroup, err := iaevents.ActivateGroup(placement, process, perfEvents)

Monitor events values

While configured events stay active, user can read measurements from them. Depending on type of event, different monitors are returned by activators. It can be ActiveEvent for core event, ActiveMultiEvent for uncore event or ActiveEventGroup for group of events.

Read core event value
value, err := activeEvent.ReadValue()
Read uncore event value

For uncore event the ReadValues method returns slice of values for all placements on given multi PMU.

values, err := activeMultiEvent.ReadValues()
Read events group

For group of events user has to iterate through every event in the group and read the value separately.

for _, activeEvent := range activeEventGroup.Events() {
    value, err := activeEvent.ReadValue()
}
Counter value

Obtained measurements are stored in CounterValue structure as three values:

  • Raw
  • Enabled
  • Running

Raw is a total count of event. Enabled and running are total time the event was enabled and running. Normally these are the same. If more events are started than available counter slots on the PMU, then multiplexing occurs and events only run part of the time. In that case the enabled and running values can be used to scale an estimated value for the counter.

For that case there is also an EventScaledValue method which calculates scaled value as: scaled = raw * enabled / running and returns the result as big.Int.

To aggregate slice of values the library provides function AggregateValues, which helps to aggregate the results across PMU units or multiple cores.

Deactivate events

To finish work with library all active events should be deactivated to close corresponding file descriptors. All types of active events have method Deactivate. After deactivation PMU counters are stopped and user can not read new values.

err := activeEvent.Deactivate()
activeMultiEvent.Deactivate()
activeEventGroup.Deactivate()

Example application

Along with the library there is provided example application iastat to show usage of iaevents package. It has features to list the events read from file and gather measurements with configured options like interval or aggregation.

Build application:

make example

More info:

./iastat -help

Usage:

./iastat [-file <file>] -listevents
./iastat [-file <file>] -events <events> -cpu <CPUs> [-interval <interval>] [-loop <N>] [-no-aggr] [-raw] [-merge]

Licensing

iaevents is distributed under the Apache License, Version 2.0.

Changelog

Version Description
v1.0.0 Init version. Golang version is 1.16
v1.1.0 New format of perfmon is supported. Update golang version to 1.19. Add new method: GetHeaders()

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func AddDefaultFiles

func AddDefaultFiles(adder FilesAdder, dir string, cpuid CPUidStringer) error

AddDefaultFiles adds core and uncore events from specified dir for provided cpuid

func AggregateValues

func AggregateValues(values []CounterValue) (raw *big.Int, enabled *big.Int, running *big.Int)

AggregateValues lets aggregate multiple counter values.

func AllCPUs

func AllCPUs() ([]int, error)

AllCPUs returns slice of all available cpus.

func AllSockets

func AllSockets() ([]int, error)

AllSockets returns slice of all available sockets.

func EventScaledValue

func EventScaledValue(values CounterValue) *big.Int

EventScaledValue calculates scaled value from CounterValue. Scaled value is equal to raw * enabled / running. If more events are activated than available counter slots on the PMU, then multiplexing happens and events only run part of the time. In that case the enabled and running values can be used to scale an estimated value for the count.

Types

type Activator

type Activator interface {
	Activate(placement PlacementProvider, targetProcess TargetProcess, options Options) (*ActiveEvent, error)
}

Activator is the interface that wraps the activate method. Activate is able to return activated (being counted) perf event as ActiveEvent. Returned active event is configured to fulfill provided configuration placement provides a specific hardware placement for event, targetProcess provides a specific process (or cgroup) for event to monitor, options specify any additional perf configuration options. Activate must return a non-nil error if event cannot be activated, or provided parameters are invalid.

type ActiveEvent

type ActiveEvent struct {
	PerfEvent      *PerfEvent
	FileDescriptor int
	Decoded        string
	// contains filtered or unexported fields
}

ActiveEvent holds active perf event. Active - means that perf event has been opened by perf_event_open syscall (https://man7.org/linux/man-pages/man2/perf_event_open.2.html) and is able to provide counter values for specific, previously settled configuration and placement.

func (*ActiveEvent) Deactivate

func (ae *ActiveEvent) Deactivate() error

Deactivate stops counting active event by closing its file descriptor.

func (*ActiveEvent) PMUName

func (ae *ActiveEvent) PMUName() string

PMUName returns name of the ActiveEvent's PMU.

func (*ActiveEvent) PMUPlacement

func (ae *ActiveEvent) PMUPlacement() (int, uint32)

func (*ActiveEvent) ReadValue

func (ae *ActiveEvent) ReadValue() (CounterValue, error)

func (*ActiveEvent) String

func (ae *ActiveEvent) String() string

type ActiveEventGroup

type ActiveEventGroup struct {
	// contains filtered or unexported fields
}

ActiveEventGroup contains set of active events belonging to the same perf event group.

func ActivateGroup

func ActivateGroup(placement PlacementProvider, targetProcess TargetProcess, events []CustomizableEvent) (*ActiveEventGroup, error)

ActivateGroup is able to create ActiveEventGroup from provided events, placement, process and options. If events size is 0 no group will be created, and error will be returned. In case of single event contained in events slice, only one event perf group will be activated. In other cases, first event in slice will be treated as a leader and rest of them will be activated with its file descriptor as group file descriptor (group_fd argument) in perf_event_open syscall (https://man7.org/linux/man-pages/man2/perf_event_open.2.html).

func (*ActiveEventGroup) Deactivate

func (ae *ActiveEventGroup) Deactivate()

func (*ActiveEventGroup) Events

func (ae *ActiveEventGroup) Events() []*ActiveEvent

func (*ActiveEventGroup) String

func (ae *ActiveEventGroup) String() string

type ActiveEventMonitor

type ActiveEventMonitor interface {
	ReadValue() (CounterValue, error)
	Deactivate() error
}

ActiveEventMonitor is and interface that wraps methods around monitoring active perf event.

ReadValue reads actual active perf event counter values and returns CounterValue. If values cannot be obtained, error is propagated. Close deactivates and closes active perf event.

type ActiveMultiEvent

type ActiveMultiEvent struct {
	// contains filtered or unexported fields
}

ActiveMultiEvent contains set of active multi PMU uncore events.

func (*ActiveMultiEvent) Deactivate

func (am *ActiveMultiEvent) Deactivate()

func (*ActiveMultiEvent) Events

func (am *ActiveMultiEvent) Events() []*ActiveEvent

func (*ActiveMultiEvent) PerfEvent

func (am *ActiveMultiEvent) PerfEvent() *PerfEvent

PerfEvent returns original multi PMU perf event.

func (*ActiveMultiEvent) ReadValues

func (am *ActiveMultiEvent) ReadValues() ([]CounterValue, error)

ReadValues returns all active multi PMU events counter value.

func (*ActiveMultiEvent) String

func (am *ActiveMultiEvent) String() string

type CPUidStringer

type CPUidStringer interface {
	Full() string
	Short() string
}

CPUidStringer interface to get the CPU ID in full form 'vendor-family-model-stepping' or short without stepping 'vendor-family-model'

func CheckCPUid

func CheckCPUid() (CPUidStringer, error)

CheckCPUid gets the CPU ID Stringer which provides the ID for current cpu in forms: 'vendor-family-model-stepping' (for example 'GenuineIntel-6-55-4') or shorter 'vendor-family-model' (for example 'GenuineIntel-6-55')

type CounterValue

type CounterValue struct {
	Raw     uint64
	Enabled uint64
	Running uint64
}

CounterValue holds specific perf event Raw, Enabled and Running counter values.

func (*CounterValue) String

func (cv *CounterValue) String() string

type CustomizableEvent

type CustomizableEvent struct {
	Event   *PerfEvent
	Options Options
}

CustomizableEvent unites PerfEvent with specific Options.

type DeprecatedFormatError added in v1.1.0

type DeprecatedFormatError struct {
	// contains filtered or unexported fields
}

DeprecatedFormatError - error to indicate if a file has the deprecated JSON format

func (*DeprecatedFormatError) Error added in v1.1.0

func (e *DeprecatedFormatError) Error() string

func (*DeprecatedFormatError) Is added in v1.1.0

func (e *DeprecatedFormatError) Is(target error) bool

Is - the implementation to support errors.Is

type EventGroupMonitor

type EventGroupMonitor interface {
	Events() []*ActiveEvent
	Deactivate()
}

EventGroupMonitor is an interface for group monitoring functionalities.

Events return all active events within event group that the EventGroupMonitor is monitoring. Deactivate closes, deactivates all active events within event group.

type EventTargetProcess

type EventTargetProcess struct {
	// contains filtered or unexported fields
}

EventTargetProcess holds target process data.

func NewEventTargetProcess

func NewEventTargetProcess(pid int, processType int) *EventTargetProcess

NewEventTargetProcess returns new EventTargetProcess.

func (*EventTargetProcess) ProcessID

func (etp *EventTargetProcess) ProcessID() int

func (*EventTargetProcess) ProcessType

func (etp *EventTargetProcess) ProcessType() int

func (*EventTargetProcess) String

func (etp *EventTargetProcess) String() string

type FilesAdder

type FilesAdder interface {
	AddFiles(firstPath string, optionalPaths ...string) error
}

FilesAdder interface to add JSON files with events

type JSONData added in v1.1.0

type JSONData struct {
	Header *JSONHeader  `json:"Header"`
	Events []*JSONEvent `json:"Events"`
}

JSONData - new data format of perfmon

type JSONEvent

type JSONEvent struct {
	AnyThread        string `json:"AnyThread"`
	BriefDescription string `json:"BriefDescription"`
	CounterMask      string `json:"CounterMask"`
	Deprecated       string `json:"Deprecated"`
	EdgeDetect       string `json:"EdgeDetect"`
	EventCode        string `json:"EventCode"`
	EventName        string `json:"EventName"`
	ExtSel           string `json:"ExtSel"`
	Invert           string `json:"Invert"`
	MSRIndex         string `json:"MSRIndex"`
	MSRValue         string `json:"MSRValue"`
	SampleAfterValue string `json:"SampleAfterValue"`
	UMask            string `json:"UMask"`
	Unit             string `json:"Unit"`
}

JSONEvent - fields in JSON definition of PMU events

type JSONFilesReader

type JSONFilesReader struct {
	// contains filtered or unexported fields
}

JSONFilesReader load events definition from specified JSON files

func NewFilesReader

func NewFilesReader() *JSONFilesReader

NewFilesReader returns new JSONFilesReader with default members

func (*JSONFilesReader) AddFiles

func (r *JSONFilesReader) AddFiles(firstPath string, optionalPaths ...string) error

AddFiles add files for Reader to read

func (*JSONFilesReader) GetHeaders added in v1.1.0

func (r *JSONFilesReader) GetHeaders() map[string]JSONHeader

GetHeaders - return the headers from event files Key of the map is the path of the file

func (*JSONFilesReader) Read

func (r *JSONFilesReader) Read() ([]*JSONEvent, error)

Read and return JSON events from path(s)

func (*JSONFilesReader) String

func (r *JSONFilesReader) String() string

type JSONHeader added in v1.1.0

type JSONHeader struct {
	Copyright     string `json:"Copyright"`
	Info          string `json:"Info"`
	DatePublished string `json:"DatePublished"`
	Version       string `json:"Version"`
	Legend        string `json:"Legend"`
}

JSONHeader - struct contains the Header info of events

type Matcher

type Matcher interface {
	Match(e *JSONEvent) (bool, error)
}

Matcher interface to match event by name or other property.

type MultiActivator

type MultiActivator interface {
	ActivateMulti(placements []PlacementProvider, process TargetProcess, options Options) (*ActiveMultiEvent, error)
}

MultiActivator is an interface that wraps ActivateMulti method.

ActivateMulti activates multi PMU perf event on specific placements. Returns this activated events as ActiveMultiEvent.

type NameMatcher

type NameMatcher struct {
	// contains filtered or unexported fields
}

NameMatcher match events by exact name.

func NewNameMatcher

func NewNameMatcher(eventsNames ...string) *NameMatcher

NewNameMatcher returns new NameMatcher with default members. If no arguments are passed the matcher will match all events.

func (*NameMatcher) Match

func (m *NameMatcher) Match(event *JSONEvent) (bool, error)

Match checks if the provided event matches the stored list of event names.

func (*NameMatcher) String

func (m *NameMatcher) String() string

type NamedPMUType

type NamedPMUType struct {
	Name    string
	PMUType uint32
}

NamedPMUType wraps PMUType with its name

type Options

type Options interface {
	PerfStatString() []string
	Attr() *unix.PerfEventAttr
}

Options is an interface for extra perf options.

PerfStatString() returns user specified extra modifiers as string. Attr() returns modified unix.PerfEventAttr struct properly set.

type OptionsBuilder

type OptionsBuilder interface {
	SetEnableOnExec(bool) OptionsBuilder
	SetAttrModifiers([]string) OptionsBuilder
	Build() (Options, error)
}

OptionsBuilder is an builder for Options.

SetEnableOnExec decides if set perf event attribute bits with PerfBitDisabled and PerfBitEnableOnExec. SetAttrModifiers updates perf event attribute qualifiers. Build builds new Options.

func NewOptions

func NewOptions() OptionsBuilder

NewOptions returns new options builder.

type PerfEvent

type PerfEvent struct {
	Name     string
	Uncore   bool
	PMUName  string
	PMUTypes []NamedPMUType

	Attr *unix.PerfEventAttr
	// contains filtered or unexported fields
}

PerfEvent contains event general data.

func NewPerfEvent

func NewPerfEvent() *PerfEvent

NewPerfEvent creates new PerfEvent.

func (*PerfEvent) Activate

func (pe *PerfEvent) Activate(placement PlacementProvider, targetProcess TargetProcess, options Options) (*ActiveEvent, error)

func (*PerfEvent) ActivateMulti

func (pe *PerfEvent) ActivateMulti(placements []PlacementProvider, process TargetProcess, options Options) (*ActiveMultiEvent, error)

func (*PerfEvent) Decoded

func (pe *PerfEvent) Decoded() string

Decoded returns a perf representation of the event

func (*PerfEvent) NewPlacements

func (pe *PerfEvent) NewPlacements(unit string, cpu int, cpus ...int) ([]PlacementProvider, error)

NewPlacements returns slice of newly created placements for provided cpus and unit. It also checks if provided unit and cpus are valid for the PerfEvent. In case of uncore events, cpus are interpreted as socket CPUs. If unit == "" it returns all units for distributed uncore PMU.

func (*PerfEvent) String

func (pe *PerfEvent) String() string

type PerfEventOptions

type PerfEventOptions struct {
	// contains filtered or unexported fields
}

PerfEventOptions contains some of the perf event configuration options and modifiers.

func (*PerfEventOptions) Attr

func (pfo *PerfEventOptions) Attr() *unix.PerfEventAttr

func (*PerfEventOptions) PerfStatString

func (pfo *PerfEventOptions) PerfStatString() []string

func (*PerfEventOptions) String

func (pfo *PerfEventOptions) String() string

type PerfTransformer

type PerfTransformer struct {
	// contains filtered or unexported fields
}

PerfTransformer uses reader and matcher to provide perf events

func NewPerfTransformer

func NewPerfTransformer() *PerfTransformer

NewPerfTransformer returns new PerfTransformer with default members

func (*PerfTransformer) Transform

func (t *PerfTransformer) Transform(reader Reader, matcher Matcher) ([]*PerfEvent, error)

Transform transforms data from reader to perf events

type Placement

type Placement struct {
	CPU     int
	PMUType uint32
}

Placement holds info about single placement for event

func (*Placement) PMUPlacement

func (p *Placement) PMUPlacement() (int, uint32)

PMUPlacement returns single placement - CPU and PMU TypeID on which the event should be placed

func (*Placement) String

func (p *Placement) String() string

type PlacementFactory

type PlacementFactory interface {
	NewPlacements(unit string, cpu int, cpus ...int) ([]PlacementProvider, error)
}

PlacementFactory interface to get placement providers for specified unit and CPUs

type PlacementProvider

type PlacementProvider interface {
	PMUPlacement() (int, uint32) // cpu_id, pmu_type
}

PlacementProvider interface to provide single placement by means of CPU and PMU TypeID

func NewCorePlacement

func NewCorePlacement(factory PlacementFactory, cpu int) (PlacementProvider, error)

NewCorePlacement creates new core placement for given CPU

func NewCorePlacements

func NewCorePlacements(factory PlacementFactory, cpu int, cpus ...int) ([]PlacementProvider, error)

NewCorePlacements creates new core placements for given CPUs

func NewUncoreAllPlacements

func NewUncoreAllPlacements(factory PlacementFactory, socket int) ([]PlacementProvider, error)

NewUncoreAllPlacements creates new uncore placements for given socket and all units available from given factory

func NewUncorePlacement

func NewUncorePlacement(factory PlacementFactory, unit string, socket int) (PlacementProvider, error)

NewUncorePlacement creates new uncore placement for given unit and socket

type Reader

type Reader interface {
	Read() ([]*JSONEvent, error)
}

Reader interface to read JSON events

type TargetProcess

type TargetProcess interface {
	ProcessID() int
	ProcessType() int
}

TargetProcess is an interface with methods specialized in gathering information about desired system process.

ProcessID returns specific process identifier. ProcessType returns type of process identifier. Value 0 is for process id (PID) and 1 is for cgroup identifier.

type TransformationError

type TransformationError struct {
	// contains filtered or unexported fields
}

func (*TransformationError) Error

func (t *TransformationError) Error() string

func (*TransformationError) Errors

func (t *TransformationError) Errors() []error

type Transformer

type Transformer interface {
	Transform(reader Reader, matcher Matcher) ([]*PerfEvent, error)
}

Transformer interface to provide perf events

Directories

Path Synopsis
examples

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL