Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement psutil-golang: CPU, Memory and Process Information Retrieval for Linux #1

Open
wants to merge 18 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
ed9d9d4
feat: initialize project structure
MohamedFadel01 Oct 3, 2024
7491609
feat: add structs for CPU, memory, and process information
MohamedFadel01 Oct 3, 2024
43aaa8d
feat: add utility function for file reading with error handling
MohamedFadel01 Oct 4, 2024
31f0a56
test: add unit tests for openAndReadFile function to validate file re…
MohamedFadel01 Oct 4, 2024
ef866d6
feat: add getFieldValue function to extract specific field values fro…
MohamedFadel01 Oct 4, 2024
d18a2f7
test: add unit tests for getFieldValue function to cover various fiel…
MohamedFadel01 Oct 4, 2024
249e853
feat: add GetMemoryInfo function to parse and extract memory informat…
MohamedFadel01 Oct 5, 2024
71ef533
test: add unit tests for GetMemoryInfo with mock file reading and err…
MohamedFadel01 Oct 5, 2024
0432137
feat: implement GetCPUInfo function for retrieving detailed CPU infor…
MohamedFadel01 Oct 5, 2024
3dca4d6
feat: implement GetProcessInfo function for retrieving process inform…
MohamedFadel01 Oct 13, 2024
d1eef0e
test: add unit tests for GetCPUInfo
MohamedFadel01 Oct 16, 2024
343ef0f
test: add unit tests for GetProcessInfo
MohamedFadel01 Oct 16, 2024
d17f3a2
refactor: reorganize project structure by moving source files into ps…
MohamedFadel01 Oct 16, 2024
f5b7bff
docs: add README with project structure and usage guide
MohamedFadel01 Oct 16, 2024
f34867d
ci: add GitHub Actions CI pipeline
MohamedFadel01 Oct 16, 2024
32ee896
fix(test): try to fix erros raised from ci test action
MohamedFadel01 Oct 16, 2024
2429763
docs: add go doc comments
MohamedFadel01 Oct 16, 2024
cd6d5c5
fix(test): try to fix erros raised from ci test action
MohamedFadel01 Oct 16, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 55 additions & 0 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
name: CI Pipeline

on:
pull_request:
branches:
- main
push:
branches:
- main
- development

jobs:
golangci:
name: Lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: stable
- name: Install golangci-lint
run: go install github.com/golangci/golangci-lint/cmd/[email protected]
- name: Run golangci-lint
run: golangci-lint run ./...

fmt:
name: Format Check
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: stable
- name: Run go fmt
run: gofmt -l .

test:
name: Test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: stable
- name: Install Dependencies
run: go mod tidy
- name: Run Tests
run: go test ./... -v -coverprofile=coverage.out
- name: Upload Test Coverage
if: success()
uses: actions/upload-artifact@v3
with:
name: coverage
path: coverage.out

1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,4 @@ go.work.sum

# env file
.env
.vscode
88 changes: 87 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,87 @@
# psutil-golang-MohamedFadel
# psutil-golang

A Go package for retrieving system and process information on Linux systems.

## Project Structure

```
.
├── go.mod
├── go.sum
├── psutils
│ ├── cpu.go
│ ├── erros.go
│ ├── internal.go
│ ├── internal_test.go
│ ├── mem.go
│ ├── proc.go
│ └── psutil_test.go
└── README.md
```

## Features

- CPU information retrieval
- Memory information retrieval
- Process information retrieval

## Installation

To use this package in your Go project, run:

```bash
go get github.com/codescalersinternships/psutil-golang-MohamedFadel
```

## Usage

Here's a basic example of how to use the package:

```go
package main

import (
"fmt"
"github.com/codescalersinternships/psutil-golang-MohamedFadel/psutils"
)

func main() {
// Get CPU info
cpuInfo, err := psutils.GetCPUInfo()
if err != nil {
fmt.Printf("Error getting CPU info: %v\n", err)
} else {
fmt.Printf("CPU Info: %+v\n", cpuInfo)
}

// Get Memory info
memInfo, err := psutils.GetMemoryInfo()
if err != nil {
fmt.Printf("Error getting memory info: %v\n", err)
} else {
fmt.Printf("Memory Info: %+v\n", memInfo)
}

// Get Process info
procInfo, err := psutils.GetProcessInfo()
if err != nil {
fmt.Printf("Error getting process info: %v\n", err)
} else {
fmt.Printf("Process Info: %+v\n", procInfo)
}
}
```

## Testing

To run the tests, navigate to the project root and run:

```bash
go test ./...
```

## Dependencies

This project uses the following external dependency:

- github.com/stretchr/testify v1.9.0 (for testing)
5 changes: 5 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module github.com/codescalersinternships/psutil-golang-MohamedFadel

go 1.23.0

require github.com/stretchr/testify v1.9.0 // indirect
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
142 changes: 142 additions & 0 deletions psutils/cpu.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
package psutils

import (
"fmt"
"io/fs"
"os"
"strconv"
"strings"
)

// CPUCoreFreq represents the frequency information for a CPU core.
type CPUCoreFreq struct {
Core string // The name of the CPU core
MinFreq int // The minimum frequency of the core in Hz
MaxFreq int // The maximum frequency of the core in Hz
}

// CPUInfo contains various information about the CPU.
type CPUInfo struct {
NumOfCores int // The number of CPU cores
ModelName string // The model name of the CPU
CacheSize int // The total cache size in KB
CPUMHz float64 // The average CPU frequency in MHz
Frequency []CPUCoreFreq // Frequency information for each core
}

var (
readFile func(name string) ([]byte, error)
readDir func(name string) ([]fs.DirEntry, error)
)

func init() {
readFile = os.ReadFile
readDir = os.ReadDir
}

/*
GetCPUInfo retrieves and returns information about the CPU.
It reads various system files to gather this information.
Returns a pointer to CPUInfo and an error if any occurred during the process.
*/
func GetCPUInfo() (*CPUInfo, error) {
var cpuInfo CPUInfo

data, err := openAndReadFile("/proc/cpuinfo")
if err != nil {
return nil, err
}

cpuInfo.ModelName = getFieldValue(data, "model name")

for _, line := range data {
if strings.Contains(line, "processor") {
cpuInfo.NumOfCores++
}
}

var totalMHz float64
for _, line := range data {
if strings.Contains(line, "cpu MHz") {
if mhz, err := strconv.ParseFloat(strings.TrimSpace(strings.Split(line, ":")[1]), 64); err == nil {
totalMHz += mhz
}
}
}
cpuInfo.CPUMHz = totalMHz / float64(cpuInfo.NumOfCores)

d, err := readFile("/sys/devices/system/cpu/cpu0/cache/index0/size")
if err != nil {
return nil, err
}

value := strings.Split(string(d), "\n")[0]
value = value[:len(value)-1]
l1iCache, err := strconv.Atoi(value)
if err != nil {
return nil, err
}

d2, err := readFile("/sys/devices/system/cpu/cpu0/cache/index1/size")
if err != nil {
return nil, err
}

value2 := strings.Split(string(d2), "\n")[0]
value2 = value2[:len(value2)-1]
l1dCache, err := strconv.Atoi(value2)
if err != nil {
return nil, err
}

d3, err := readFile("/sys/devices/system/cpu/cpu0/cache/index2/size")
if err != nil {
return nil, err
}

value3 := strings.Split(string(d3), "\n")[0]
value3 = value3[:len(value3)-1]
l2Cache, err := strconv.Atoi(value3)
if err != nil {
return nil, err
}

d4, err := readFile("/sys/devices/system/cpu/cpu0/cache/index3/size")
if err != nil {
return nil, err
}

value4 := strings.Split(string(d4), "\n")[0]
value4 = value4[:len(value4)-1]
l3Cache, err := strconv.Atoi(value4)
if err != nil {
return nil, err
}

cpuInfo.CacheSize = cpuInfo.NumOfCores*(l1iCache+l1dCache+l2Cache) + l3Cache

for i := 0; i < cpuInfo.NumOfCores; i++ {
var cpuCoreFreq CPUCoreFreq
cpuCoreFreq.Core = fmt.Sprintf("cpu%d", i)

dataMin, err := readFile(fmt.Sprintf("/sys/devices/system/cpu/cpu%d/cpufreq/cpuinfo_min_freq", i))
if err != nil {
return nil, err
}
if minFreq, err := strconv.Atoi(strings.TrimSpace(string(dataMin))); err == nil {
cpuCoreFreq.MinFreq = minFreq
}

dataMax, err := readFile(fmt.Sprintf("/sys/devices/system/cpu/cpu%d/cpufreq/cpuinfo_max_freq", i))
if err != nil {
return nil, err
}
if maxFreq, err := strconv.Atoi(strings.TrimSpace(string(dataMax))); err == nil {
cpuCoreFreq.MaxFreq = maxFreq
}

cpuInfo.Frequency = append(cpuInfo.Frequency, cpuCoreFreq)
}

return &cpuInfo, nil
}
7 changes: 7 additions & 0 deletions psutils/erros.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package psutils

import "errors"

var (
ErrReadingFile = errors.New("error reading file")
)
31 changes: 31 additions & 0 deletions psutils/internal.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package psutils

import (
"fmt"
"os"
"strings"
)

var openAndReadFile = func(path string) ([]string, error) {
data, err := os.ReadFile(path)
if err != nil {
return nil, fmt.Errorf("%w: %v", ErrReadingFile, err)
}

lines := strings.Split(string(data), "\n")
return lines, nil
}

func getFieldValue(lines []string, field string) string {
for _, l := range lines {
line := strings.Split(l, ":")

if strings.TrimSpace(line[0]) != field {
continue
}

return strings.TrimSpace(line[1])
}

return ""
}
Loading
Loading