Skip to content
This repository was archived by the owner on Dec 15, 2025. It is now read-only.

Commit 1d1ba60

Browse files
committed
...
1 parent bf1d79a commit 1d1ba60

12 files changed

Lines changed: 637 additions & 8 deletions

File tree

.github/workflows/release-osx.yaml

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
on:
2+
push:
3+
# Sequence of patterns matched against refs/tags
4+
tags:
5+
- "v*" # Push events to matching v*, i.e. v1.0, v20.15.10
6+
7+
name: Create OSX Release
8+
9+
jobs:
10+
create-release:
11+
name: Create Release
12+
runs-on: ubuntu-latest
13+
outputs:
14+
upload_url: ${{ steps.create_release.outputs.upload_url }}
15+
steps:
16+
- name: Create Release
17+
id: create_release
18+
uses: actions/create-release@v1
19+
env:
20+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
21+
with:
22+
tag_name: ${{ github.ref }}
23+
release_name: Release ${{ github.ref }} - OSX
24+
draft: false
25+
prerelease: false
26+
27+
build:
28+
name: Build and Upload OSX Binaries
29+
runs-on: macos-latest
30+
needs: create-release
31+
strategy:
32+
matrix:
33+
target:
34+
- x86_64-apple-darwin
35+
- aarch64-apple-darwin
36+
steps:
37+
- name: Checkout code
38+
uses: actions/checkout@v4
39+
- name: Install Rust toolchain
40+
uses: actions-rs/toolchain@v1
41+
with:
42+
toolchain: stable
43+
target: ${{ matrix.target }}
44+
override: true
45+
- name: Build release
46+
uses: actions-rs/cargo@v1
47+
with:
48+
command: build
49+
args: --release --target=${{ matrix.target }}
50+
- name: Strip binary
51+
run: |
52+
strip target/${{ matrix.target }}/release/zinit
53+
- name: Upload Release Asset
54+
uses: actions/upload-release-asset@v1
55+
env:
56+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
57+
with:
58+
upload_url: ${{ needs.create-release.outputs.upload_url }}
59+
asset_path: target/${{ matrix.target }}/release/zinit
60+
asset_name: zinit-${{ matrix.target }}
61+
asset_content_type: application/octet-stream

docs/stats.md

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
# Service Stats Functionality
2+
3+
This document describes the stats functionality in Zinit, which provides memory and CPU usage information for services and their child processes.
4+
5+
## Overview
6+
7+
The stats functionality allows you to monitor the resource usage of services managed by Zinit. It provides information about:
8+
9+
- Memory usage (in bytes)
10+
- CPU usage (as a percentage)
11+
- Child processes and their resource usage
12+
13+
This is particularly useful for monitoring system resources and identifying services that might be consuming excessive resources.
14+
15+
## Command Line Usage
16+
17+
To get stats for a service using the command line:
18+
19+
```bash
20+
zinit stats <service-name>
21+
```
22+
23+
Example:
24+
```bash
25+
zinit stats nginx
26+
```
27+
28+
This will output YAML-formatted stats information:
29+
30+
```yaml
31+
name: nginx
32+
pid: 1234
33+
memory_usage: 10485760 # Memory usage in bytes (10MB)
34+
cpu_usage: 2.5 # CPU usage as percentage
35+
children: # Stats for child processes
36+
- pid: 1235
37+
memory_usage: 5242880
38+
cpu_usage: 1.2
39+
- pid: 1236
40+
memory_usage: 4194304
41+
cpu_usage: 0.8
42+
```
43+
44+
## JSON-RPC API
45+
46+
The stats functionality is also available through the JSON-RPC API:
47+
48+
### Method: `service_stats`
49+
50+
Get memory and CPU usage statistics for a service.
51+
52+
**Parameters:**
53+
- `name` (string, required): The name of the service to get stats for
54+
55+
**Returns:**
56+
- Object containing stats information:
57+
- `name` (string): Service name
58+
- `pid` (integer): Process ID of the service
59+
- `memory_usage` (integer): Memory usage in bytes
60+
- `cpu_usage` (number): CPU usage as a percentage (0-100)
61+
- `children` (array): Stats for child processes
62+
- Each child has:
63+
- `pid` (integer): Process ID of the child process
64+
- `memory_usage` (integer): Memory usage in bytes
65+
- `cpu_usage` (number): CPU usage as a percentage (0-100)
66+
67+
**Example Request:**
68+
```json
69+
{
70+
"jsonrpc": "2.0",
71+
"id": 1,
72+
"method": "service_stats",
73+
"params": {
74+
"name": "nginx"
75+
}
76+
}
77+
```
78+
79+
**Example Response:**
80+
```json
81+
{
82+
"jsonrpc": "2.0",
83+
"id": 1,
84+
"result": {
85+
"name": "nginx",
86+
"pid": 1234,
87+
"memory_usage": 10485760,
88+
"cpu_usage": 2.5,
89+
"children": [
90+
{
91+
"pid": 1235,
92+
"memory_usage": 5242880,
93+
"cpu_usage": 1.2
94+
},
95+
{
96+
"pid": 1236,
97+
"memory_usage": 4194304,
98+
"cpu_usage": 0.8
99+
}
100+
]
101+
}
102+
}
103+
```
104+
105+
**Possible Errors:**
106+
- `-32000`: Service not found
107+
- `-32003`: Service is down
108+
109+
## Implementation Details
110+
111+
The stats functionality works by:
112+
113+
1. Reading process information from `/proc/<pid>/` directories on Linux systems
114+
2. Calculating memory usage from `/proc/<pid>/status` (VmRSS field)
115+
3. Calculating CPU usage by sampling `/proc/<pid>/stat` over a short interval
116+
4. Identifying child processes by checking the PPid field in `/proc/<pid>/status`
117+
118+
On non-Linux systems, the functionality provides placeholder values as the `/proc` filesystem is specific to Linux.
119+
120+
## Notes
121+
122+
- Memory usage is reported in bytes
123+
- CPU usage is reported as a percentage (0-100)
124+
- The service must be running to get stats (otherwise an error is returned)
125+
- Child processes are identified by their parent PID matching the service's PID

example/example.sh

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -69,11 +69,11 @@ echo "Telling zinit to monitor the service..."
6969
echo "Listing zinit services to verify..."
7070
"$ZINIT_BIN" list
7171

72-
# Step 6: Clean up (optional, but good for examples)
73-
echo "Cleaning up: stopping and forgetting $SERVICE_NAME..."
74-
"$ZINIT_BIN" stop "$SERVICE_NAME" > /dev/null 2>&1
75-
"$ZINIT_BIN" forget "$SERVICE_NAME" > /dev/null 2>&1
76-
rm -f "$SERVICE_FILE"
77-
echo "Cleanup complete."
72+
# # Step 6: Clean up (optional, but good for examples)
73+
# echo "Cleaning up: stopping and forgetting $SERVICE_NAME..."
74+
# "$ZINIT_BIN" stop "$SERVICE_NAME" > /dev/null 2>&1
75+
# "$ZINIT_BIN" forget "$SERVICE_NAME" > /dev/null 2>&1
76+
# rm -f "$SERVICE_FILE"
77+
# echo "Cleanup complete."
7878

7979
echo "--- Script Finished ---"

openrpc.json

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -601,6 +601,105 @@
601601
}
602602
]
603603
},
604+
{
605+
"name": "service_stats",
606+
"description": "Get memory and CPU usage statistics for a service",
607+
"params": [
608+
{
609+
"name": "name",
610+
"description": "The name of the service to get stats for",
611+
"required": true,
612+
"schema": {
613+
"type": "string"
614+
}
615+
}
616+
],
617+
"result": {
618+
"name": "ServiceStats",
619+
"description": "Memory and CPU usage statistics for the service",
620+
"schema": {
621+
"type": "object",
622+
"properties": {
623+
"name": {
624+
"type": "string",
625+
"description": "Service name"
626+
},
627+
"pid": {
628+
"type": "integer",
629+
"description": "Process ID of the service"
630+
},
631+
"memory_usage": {
632+
"type": "integer",
633+
"description": "Memory usage in bytes"
634+
},
635+
"cpu_usage": {
636+
"type": "number",
637+
"description": "CPU usage as a percentage (0-100)"
638+
},
639+
"children": {
640+
"type": "array",
641+
"description": "Stats for child processes",
642+
"items": {
643+
"type": "object",
644+
"properties": {
645+
"pid": {
646+
"type": "integer",
647+
"description": "Process ID of the child process"
648+
},
649+
"memory_usage": {
650+
"type": "integer",
651+
"description": "Memory usage in bytes"
652+
},
653+
"cpu_usage": {
654+
"type": "number",
655+
"description": "CPU usage as a percentage (0-100)"
656+
}
657+
}
658+
}
659+
}
660+
}
661+
}
662+
},
663+
"examples": [
664+
{
665+
"name": "Get stats for redis service",
666+
"params": [
667+
{
668+
"name": "name",
669+
"value": "redis"
670+
}
671+
],
672+
"result": {
673+
"name": "ServiceStatsResult",
674+
"value": {
675+
"name": "redis",
676+
"pid": 1234,
677+
"memory_usage": 10485760,
678+
"cpu_usage": 2.5,
679+
"children": [
680+
{
681+
"pid": 1235,
682+
"memory_usage": 5242880,
683+
"cpu_usage": 1.2
684+
}
685+
]
686+
}
687+
}
688+
}
689+
],
690+
"errors": [
691+
{
692+
"code": -32000,
693+
"message": "Service not found",
694+
"data": "service name \"unknown\" unknown"
695+
},
696+
{
697+
"code": -32003,
698+
"message": "Service is down",
699+
"data": "service \"redis\" is down"
700+
}
701+
]
702+
},
604703
{
605704
"name": "system_start_http_server",
606705
"description": "Start an HTTP/RPC server at the specified address",

src/app/api.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,26 @@ pub struct Status {
3737
pub after: HashMap<String, String>,
3838
}
3939

40+
/// Service stats information
41+
#[derive(Clone, Debug, Deserialize, Serialize)]
42+
#[serde(rename_all = "lowercase")]
43+
pub struct Stats {
44+
pub name: String,
45+
pub pid: u32,
46+
pub memory_usage: u64,
47+
pub cpu_usage: f32,
48+
pub children: Vec<ChildStats>,
49+
}
50+
51+
/// Child process stats information
52+
#[derive(Clone, Debug, Deserialize, Serialize)]
53+
#[serde(rename_all = "lowercase")]
54+
pub struct ChildStats {
55+
pub pid: u32,
56+
pub memory_usage: u64,
57+
pub cpu_usage: f32,
58+
}
59+
4060
pub struct ApiServer {
4161
_handle: ServerHandle,
4262
}

src/app/mod.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,13 @@ pub async fn kill(socket: &str, name: String, signal: String) -> Result<()> {
177177
Ok(())
178178
}
179179

180+
pub async fn stats(socket: &str, name: String) -> Result<()> {
181+
let client = IpcClientBuilder::default().build(socket.into()).await?;
182+
let results = client.stats(name).await?;
183+
encoder::to_writer(std::io::stdout(), &results)?;
184+
Ok(())
185+
}
186+
180187
pub async fn logs(
181188
socket: &str,
182189
filter: Option<String>,

src/app/rpc.rs

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use crate::app::api::Status;
1+
use crate::app::api::{Status, Stats, ChildStats};
22
use crate::zinit::config;
33
use async_trait::async_trait;
44
use jsonrpsee::core::{RpcResult, SubscriptionResult};
@@ -84,6 +84,10 @@ pub trait ZinitServiceApi {
8484
/// Get the content of a service configuration file as a JSON Value.
8585
#[method(name = "get")]
8686
async fn get(&self, name: String) -> RpcResult<Value>;
87+
88+
/// Get memory and CPU usage statistics for a service.
89+
#[method(name = "stats")]
90+
async fn stats(&self, name: String) -> RpcResult<Stats>;
8791
}
8892

8993
#[async_trait]
@@ -273,6 +277,28 @@ impl ZinitServiceApiServer for Api {
273277

274278
Ok(json_value)
275279
}
280+
281+
async fn stats(&self, name: String) -> RpcResult<Stats> {
282+
let stats = self
283+
.zinit
284+
.stats(&name)
285+
.await
286+
.map_err(|_| ErrorObjectOwned::from(ErrorCode::InternalError))?;
287+
288+
let result = Stats {
289+
name: name.clone(),
290+
pid: stats.pid as u32,
291+
memory_usage: stats.memory_usage,
292+
cpu_usage: stats.cpu_usage,
293+
children: stats.children.into_iter().map(|child| ChildStats {
294+
pid: child.pid as u32,
295+
memory_usage: child.memory_usage,
296+
cpu_usage: child.cpu_usage,
297+
}).collect(),
298+
};
299+
300+
Ok(result)
301+
}
276302
}
277303

278304
/// RPC methods for system-level operations.

0 commit comments

Comments
 (0)