Integrating Intel® Power Gadget library into Go module
As mentioned in previous article I have several computers in my home office and I would like to collect and aggregate their logs and metric information.
The biggest problem for me is the Hackintosh system as it is running macOS and there are not many open source solutions for my problem or those are incompatible because I am running the Hackintosh version of macOS with custom hardware.
The most important metrics for me in this machine are Intel processor utilization, frequency, and temperature metrics. These metrics are really well provided by the Intel Power Gadget application. I was racking my brains how I could utilize this application to collect the data from it when I have found that there are a framework and headers available for integrating this library in your application.
Next Steps
Now that I know that I can collect the metrics from the Intel Power Gadget framework, now I have to decide with which programming language I should try to integrate.
Quick looking at the services I will be using for storing metrics in my case Elasticsearch (Metricbeats), InfluxDB (Telegraf) both metric collector tools Metricbeats and Telegraf are written in Golang. I am a complete beginner with Golang and never have properly used it apart editing a few things in existing projects in my previous companies.
I have a few options to choose from now:
- Create a plugin for the collector tool (Write it in Golang)
- Create a C utility application that would spit out JSON/CSV data into some log file and configure those metrics to consume it.
After careful thinking, I decided maybe it's time to learn Golang as it is a moderately mature language compared to back when I was just touching Golang.
Preparation
There are a few preparation steps before I can dive into the code.
Install & configure Golang
Install Intel Power Gadget application
1, 2, 3 Code!
Now that all dependencies are downloaded and installed we can start making a go module as I would like to have this as a module so it could be reused later on in different projects.
Let's create a new go module
The next step is to figure the IPG framework and it's header's location. After looking at typical macOS framework location.
I found it at /Library/Frameworks
folder and headers were in /Library/Frameworks/IntelPowerGadget.framework/Headers
path.
After looking into the headers folder I have found 3 interfaces available. After careful looking, I decided to pick the PowerGadgetLib.h
header file as it had almost all required metrics available.
Module code
Now that we have found the framework and header file we can begin writing actual Go code. After looking into go documentation about how to do C bindings I found it was pretty easy and convenient compared to other languages.
After spending time researching the available metrics, value and other data fields. I started defining structures I will be providing with this module.
One of the features I learned and really liked was that you can alias other types.
Now that we have basic unit types available we can look further in data structures.
The IDP framework provides few ways to get metrics about the processor:
- Metric at the current time
- Metric from between two samples which provides mean, min, max values
So I would need a structure for Frequency/Temperature with mean/min/max values.
An interesting point I discovered while looking into the IDP header is that processor power values are returning in Watts and Joule's values.
Another interesting point about go structures I found is that you can define a JSON field name just by using an annotation style format.
While trying to figure out how to present the available data. I found that the IPG framework returns the number of packages (processors) available in the system and then you use the further methods to fetch the metrics using package number. So metrics are also available per package and core.
I am digging the Go time module and how it has the main unit and how it is easy to transform it into other types quickly.
I also liked how the Maps are defined in Go reminds me a bit of Java.
Now that our data structures are defined we can dig into logic and functions.
Logic
To start using the IDP framework you first need to initialize it and after using shutdown it.
I have decided to make it more usable than direct integration to C functions that way it would be easier to use for everyone else.
The main function will be to GetPackages()
which will return the all packages in map[coreNumber:int]IntelPowerGadgetPackage
type.
These functions will provide basic information about processors inside the system in which this code is running.
Now to collect metrics I need to implement sampling functions. As I did with package information I decided to collect all metrics in a single function call instead of needing to call each function individually.
First, you will get all packages, when you call for each package function StartSampling
to get sampleId
which you can fetch results back from FinishSampling
which will return the IntelPowerGadgetSample
structure.
Finally, the module is finished now let's try to test it out.
Testing
After pushing code to GitHub and making a release we can finally test the module to see how it works.
Now to build the code and execute it:
The code should print out this JSON
Summary
The code is available on my personal GitHub account. The next step for this module will be integrating this module as plugin for Telegraf to log the metrics into InfluxDB.