Skip to content

Commit 8b20d7a

Browse files
committed
Address netipc review findings
1 parent d2b6d66 commit 8b20d7a

15 files changed

Lines changed: 202 additions & 74 deletions

File tree

.agents/sow/current/SOW-0015-20260605-codacy-scope-and-maintainability.md

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -544,6 +544,55 @@ Plan:
544544
7. Validate Netdata build/tests for the vendored NetIPC surface, including Windows compile/unit validation if the Netdata workflow exposes it locally.
545545
8. Commit only explicit Netdata vendor/update files, push the branch, and open a PR against Netdata.
546546

547+
### 2026-06-07 - PR 22649 Review And SonarCloud Follow-up
548+
549+
Netdata PR #22649 review context was checked before editing:
550+
551+
- GitHub review threads had two unresolved, non-outdated, actionable findings:
552+
- `src/crates/netipc/src/service/raw/server.rs:95` equivalent vendored Windows raw server shutdown risk: only the initially stored listener handle was available to `stop()`, while later accepted pipe instances could keep blocking.
553+
- `src/go/pkg/netipc/service/raw/cgroups_lookup.go:123` service dispatch had a dead `builder.Finish() == 0` check after builder error and item-count checks.
554+
- Same-pattern service dispatch review was checked in Go raw apps lookup; the same dead service-level guard existed there.
555+
- Protocol-level Go lookup dispatch was also checked. The guard looked similar, but `cd src/go && go test -count=1 ./...` proved it is required: `TestLookupDispatchCoverage` intentionally corrupts builder state and expects `ErrOverflow`. The protocol-level guards were restored.
556+
- SonarCloud PR query reported six open issues:
557+
- Go parameter-count findings in `src/go/pkg/netipc/protocol/apps_lookup.go`.
558+
- Go unnecessary recovered variable findings in `src/go/pkg/netipc/service/raw/server_unix.go` and `src/go/pkg/netipc/service/raw/server_windows.go`.
559+
- C `openat()` descriptor finding in `src/libnetdata/netipc/src/transport/posix/netipc_shm.c`.
560+
- C memcpy bounds finding in `src/libnetdata/netipc/src/transport/posix/netipc_uds_receive.c`.
561+
- GitHub AI quality findings also reported:
562+
- ignored `clock_gettime` syscall result in `bench/drivers/go/main.go`.
563+
- Go integer `range` compatibility concerns in Go service/raw and stale-dial code.
564+
- missing rationale around the Go UDS 32-bit total-length limit.
565+
- maintainability issues in `vendor-to-netdata.sh`.
566+
567+
Implemented SDK follow-up:
568+
569+
- Aligned all plugin-ipc Go modules to Netdata's Go version, `go 1.26.0`.
570+
- Ran Go's `go fix` modernization on the affected Go module surfaces, including benchmark driver loop and slice cleanups.
571+
- Changed Go benchmark CPU-time helper to check the `clock_gettime` syscall error path and return zero on failure.
572+
- Added the missing Go UDS comment explaining the protocol's 32-bit framed total-length contract.
573+
- Hardened `vendor-to-netdata.sh`:
574+
- validates source `go.mod` presence and parses module paths from the `module` directive.
575+
- avoids fragile status-line spacing.
576+
- counts only relevant C, Rust, and Go source files.
577+
- prints an import-rewrite review command after the suggested `sed` command.
578+
- Updated Rust Windows raw server to refresh the stored listener handle around each accept and clear it before joining session threads, so `stop()` can target the currently blocking listener handle.
579+
- Removed dead service-level Go lookup dispatch `builder.Finish() == 0` checks in raw apps lookup and raw cgroups lookup.
580+
- Reduced SonarCloud Go parameter count in apps lookup semantic validators by passing a private `appsLookupSemantics` struct.
581+
- Removed unnecessary recovered-value variables from Go raw server panic-recovery paths.
582+
- Avoided `openat()` with an invalid directory descriptor in POSIX SHM stale cleanup.
583+
- Added explicit chunk and first-packet bounds checks in POSIX UDS receive before copying chunk payload data into the assembled receive buffer.
584+
585+
Validation completed for this follow-up:
586+
587+
- `cd bench/drivers/go && go fix ./... && go test ./...`: passed.
588+
- `git diff --check`: passed.
589+
- `cd src/go && go test -count=1 ./...`: passed.
590+
- `cargo test --manifest-path src/crates/netipc/Cargo.toml`: 332 tests passed.
591+
- `cmake --build build`: passed.
592+
- `/usr/bin/ctest --test-dir build --output-on-failure`: 46/46 tests passed.
593+
- Win11 temp-copy Rust validation: `cargo test --manifest-path src/crates/netipc/Cargo.toml service::raw -- --test-threads=1`: 22 Windows raw-service tests passed.
594+
- Win11 temp-copy Go validation: `cd src/go && CGO_ENABLED=0 go test -count=1 ./pkg/netipc/service/raw ./pkg/netipc/transport/windows`: passed.
595+
547596
## Validation
548597

549598
Acceptance criteria evidence:
@@ -555,6 +604,8 @@ Acceptance criteria evidence:
555604
- send chunk count no longer forms `(remaining + chunk_payload_budget - 1)`.
556605
- Same-pattern C transport issue was addressed in `src/libnetdata/netipc/src/transport/windows/netipc_named_pipe.c`.
557606
- Vendor script now copies the full vendored C include/source subtrees so split C files are not missed.
607+
- Netdata PR #22649 review and SonarCloud findings were verified against SDK source and addressed in the SDK before re-vendoring.
608+
- Plugin-ipc Go modules now match Netdata's Go version, `go 1.26.0`.
558609

559610
Tests or equivalent validation:
560611

@@ -563,6 +614,9 @@ Tests or equivalent validation:
563614
- `cargo test --manifest-path src/crates/netipc/Cargo.toml`: 332 passed.
564615
- `cd src/go && go test ./...`: passed.
565616
- `cd bench/drivers/go && go test ./...`: passed.
617+
- `cd src/go && go test -count=1 ./...`: passed after restoring the required protocol-level lookup dispatch guard.
618+
- Win11 temp-copy Rust raw-service validation: 22 tests passed.
619+
- Win11 temp-copy Go service/raw and transport/windows validation: passed.
566620
- `git diff --check`: passed.
567621
- `bash -n tests/run-windows-bench.sh tests/test_windows_bench_stability_policy.sh vendor-to-netdata.sh`: passed.
568622
- `codacy-analysis analyze --files ... --output-format json`: 0 issues; known local Revive adapter invocation error remains.
@@ -576,13 +630,17 @@ Real-use evidence:
576630
Reviewer findings:
577631

578632
- GitHub AI findings for `netipc_uds_send.c` were manually verified and addressed.
633+
- GitHub review-thread findings from Netdata PR #22649 were manually verified and addressed where they were real.
634+
- SonarCloud PR findings for Netdata PR #22649 were queried and addressed in the SDK source before re-vendoring.
579635
- No external AI reviewer was used for this increment.
580636

581637
Same-failure scan:
582638

583639
- Same C total-message-width helper pattern was found and fixed in the Windows named-pipe transport.
584640
- Same chunk-count overflow-prone ceil pattern was found and fixed in POSIX C, Windows C, POSIX Rust, Windows Rust, POSIX Go, and Windows Go transport paths.
585641
- Same benchmark batch sizing nondeterminism pattern was found and fixed in POSIX and Windows Go benchmark drivers.
642+
- Same dead Go service-level lookup dispatch guard was found and removed in apps lookup and cgroups lookup.
643+
- Similar Go protocol-level lookup dispatch guards were tested and kept because regression coverage proves they still guard an overflow state.
586644

587645
Sensitive data gate:
588646

bench/drivers/go/go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
module github.com/netdata/plugin-ipc/bench/drivers/go
22

3-
go 1.25.11
3+
go 1.26.0
44

55
require github.com/netdata/plugin-ipc/go v0.0.0
66

bench/drivers/go/main.go

Lines changed: 25 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import (
1515
"os"
1616
"os/signal"
1717
"runtime"
18-
"sort"
18+
"slices"
1919
"strconv"
2020
"strings"
2121
"sync/atomic"
@@ -67,7 +67,10 @@ func cacheHashName(name string) uint32 {
6767
func cpuNS() uint64 {
6868
var ts syscall.Timespec
6969
// CLOCK_PROCESS_CPUTIME_ID = 2 on Linux
70-
_, _, _ = syscall.Syscall(syscall.SYS_CLOCK_GETTIME, 2, uintptr(unsafe.Pointer(&ts)), 0) // #nosec G103 -- clock_gettime requires passing a Timespec pointer.
70+
_, _, errno := syscall.Syscall(syscall.SYS_CLOCK_GETTIME, 2, uintptr(unsafe.Pointer(&ts)), 0) // #nosec G103 -- clock_gettime requires passing a Timespec pointer.
71+
if errno != 0 {
72+
return 0
73+
}
7174
if ts.Sec < 0 || ts.Nsec < 0 {
7275
return 0
7376
}
@@ -161,7 +164,7 @@ func (lr *latencyRecorder) percentile(pct float64) uint64 {
161164
if len(lr.samples) == 0 {
162165
return 0
163166
}
164-
sort.Slice(lr.samples, func(i, j int) bool { return lr.samples[i] < lr.samples[j] })
167+
slices.Sort(lr.samples)
165168
idx := int(pct / 100.0 * float64(len(lr.samples)-1))
166169
if idx >= len(lr.samples) {
167170
idx = len(lr.samples) - 1
@@ -291,7 +294,7 @@ func initSnapshotTemplate() bool {
291294

292295
snapshotNames = make([][]byte, 16)
293296
snapshotPaths = make([][]byte, 16)
294-
for i := uint32(0); i < 16; i++ {
297+
for i := range uint32(16) {
295298
name := fmt.Sprintf("cgroup-%d", i)
296299
path := fmt.Sprintf("/sys/fs/cgroup/bench/cg-%d", i)
297300
snapshotNames[i] = []byte(name)
@@ -313,7 +316,7 @@ func snapshotHandler() cgroups.Handler {
313316
return false
314317
}
315318
builder.SetHeader(1, atomic.AddUint64(&snapshotGen, 1))
316-
for i := uint32(0); i < 16; i++ {
319+
for i := range uint32(16) {
317320
if err := builder.Add(1000+i, 0, i%2, snapshotNames[i], snapshotPaths[i]); err != nil {
318321
return false
319322
}
@@ -426,7 +429,7 @@ func runBatchPingPongClient(runDir, service string, profiles uint32, durationSec
426429
cfg := batchClientConfig(profiles)
427430
session, err := posix.Connect(runDir, service, &cfg)
428431
if err != nil {
429-
for i := 0; i < 200; i++ {
432+
for range 200 {
430433
time.Sleep(10 * time.Millisecond)
431434
session, err = posix.Connect(runDir, service, &cfg)
432435
if err == nil {
@@ -444,7 +447,7 @@ func runBatchPingPongClient(runDir, service string, profiles uint32, durationSec
444447
var shm *posix.ShmContext
445448
if session.SelectedProfile == protocol.ProfileSHMHybrid ||
446449
session.SelectedProfile == protocol.ProfileSHMFutex {
447-
for i := 0; i < 200; i++ {
450+
for range 200 {
448451
shmCtx, serr := posix.ShmClientAttach(runDir, service, session.SessionID)
449452
if serr == nil {
450453
shm = shmCtx
@@ -490,7 +493,7 @@ func runBatchPingPongClient(runDir, service string, profiles uint32, durationSec
490493
bb.Reset(reqBuf, batchSize)
491494

492495
buildOK := true
493-
for i := uint32(0); i < batchSize; i++ {
496+
for i := range batchSize {
494497
val := counter + uint64(i)
495498
protocol.IncrementEncode(val, itemBuf)
496499
expected[i] = val + 1
@@ -578,7 +581,7 @@ func runBatchPingPongClient(runDir, service string, profiles uint32, durationSec
578581
batchOK = false
579582
}
580583
} else {
581-
for i := uint32(0); i < batchSize; i++ {
584+
for i := range batchSize {
582585
item, gerr := protocol.BatchItemGet(respPayload, batchSize, i)
583586
if gerr != nil {
584587
errors++
@@ -635,7 +638,7 @@ func runBatchPingPongClient(runDir, service string, profiles uint32, durationSec
635638
batchOK = false
636639
}
637640
} else {
638-
for i := uint32(0); i < batchSize; i++ {
641+
for i := range batchSize {
639642
item, gerr := protocol.BatchItemGet(payload, batchSize, i)
640643
if gerr != nil {
641644
errors++
@@ -693,7 +696,7 @@ func runPipelineClient(runDir, service string, durationSec int, targetRPS uint64
693696
cfg := clientConfig(profileUDS)
694697
session, err := posix.Connect(runDir, service, &cfg)
695698
if err != nil {
696-
for i := 0; i < 200; i++ {
699+
for range 200 {
697700
time.Sleep(10 * time.Millisecond)
698701
session, err = posix.Connect(runDir, service, &cfg)
699702
if err == nil {
@@ -727,7 +730,7 @@ func runPipelineClient(runDir, service string, durationSec int, targetRPS uint64
727730

728731
// Send `depth` requests with unique message IDs
729732
sendOK := true
730-
for d := 0; d < depth; d++ {
733+
for d := range depth {
731734
val := counter + uint64(d)
732735
binary.NativeEndian.PutUint64(reqPayload[:], val)
733736

@@ -753,7 +756,7 @@ func runPipelineClient(runDir, service string, durationSec int, targetRPS uint64
753756
}
754757

755758
// Receive `depth` responses
756-
for d := 0; d < depth; d++ {
759+
for d := range depth {
757760
_, payload, rerr := session.Receive(recvBuf)
758761
if rerr != nil {
759762
errors++
@@ -812,7 +815,7 @@ func runPipelineBatchClient(runDir, service string, durationSec int, targetRPS u
812815
cfg := batchClientConfig(profileUDS)
813816
session, err := posix.Connect(runDir, service, &cfg)
814817
if err != nil {
815-
for i := 0; i < 200; i++ {
818+
for range 200 {
816819
time.Sleep(10 * time.Millisecond)
817820
session, err = posix.Connect(runDir, service, &cfg)
818821
if err == nil {
@@ -852,14 +855,14 @@ func runPipelineBatchClient(runDir, service string, durationSec int, targetRPS u
852855

853856
// Build and send `depth` batch requests
854857
sendOK := true
855-
for d := 0; d < depth; d++ {
858+
for d := range depth {
856859
bs := randomBatchSize(&rng)
857860
batchSizes[d] = bs
858861

859862
var bb protocol.BatchBuilder
860863
bb.Reset(reqBufs[d], bs)
861864

862-
for i := uint32(0); i < bs; i++ {
865+
for i := range bs {
863866
protocol.IncrementEncode(counter+uint64(i), itemBuf)
864867
if err := bb.Add(itemBuf); err != nil {
865868
sendOK = false
@@ -897,7 +900,7 @@ func runPipelineBatchClient(runDir, service string, durationSec int, targetRPS u
897900
}
898901

899902
// Receive `depth` batch responses
900-
for d := 0; d < depth; d++ {
903+
for d := range depth {
901904
_, _, rerr := session.Receive(recvBuf)
902905
if rerr != nil {
903906
errors++
@@ -941,7 +944,7 @@ func runPingPongClient(runDir, service string, profiles uint32, durationSec int,
941944
session, err := posix.Connect(runDir, service, &cfg)
942945
if err != nil {
943946
// Retry
944-
for i := 0; i < 200; i++ {
947+
for range 200 {
945948
time.Sleep(10 * time.Millisecond)
946949
session, err = posix.Connect(runDir, service, &cfg)
947950
if err == nil {
@@ -959,7 +962,7 @@ func runPingPongClient(runDir, service string, profiles uint32, durationSec int,
959962
var shm *posix.ShmContext
960963
if session.SelectedProfile == protocol.ProfileSHMHybrid ||
961964
session.SelectedProfile == protocol.ProfileSHMFutex {
962-
for i := 0; i < 200; i++ {
965+
for range 200 {
963966
shmCtx, serr := posix.ShmClientAttach(runDir, service, session.SessionID)
964967
if serr == nil {
965968
shm = shmCtx
@@ -1093,7 +1096,7 @@ func runPingPongClient(runDir, service string, profiles uint32, durationSec int,
10931096
func runSnapshotClient(runDir, service string, profiles uint32, durationSec int, targetRPS uint64, scenario, serverLang string) int {
10941097
client := cgroups.NewClient(runDir, service, typedClientConfig(profiles))
10951098

1096-
for i := 0; i < 200; i++ {
1099+
for range 200 {
10971100
client.Refresh()
10981101
if client.Ready() {
10991102
break
@@ -1315,8 +1318,8 @@ func runLookupMethodBench(durationSec int, scenario string, targetRPS uint64) in
13151318

13161319
paths := make([][]byte, itemCount)
13171320
pids := make([]uint32, itemCount)
1318-
for i := 0; i < itemCount; i++ {
1319-
paths[i] = []byte(fmt.Sprintf("/sys/fs/cgroup/bench/cg-%03d", i))
1321+
for i := range itemCount {
1322+
paths[i] = fmt.Appendf(nil, "/sys/fs/cgroup/bench/cg-%03d", i)
13201323
pids[i] = uint32(1000 + i)
13211324
}
13221325

src/crates/netipc/src/service/raw/server_windows.rs

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ impl ManagedServer {
1818
)
1919
.map_err(|_| NipcError::BadLayout)?;
2020

21-
*self.listener_handle.lock().unwrap() = Some(listener.handle() as usize);
21+
self.remember_windows_listener_handle(&listener);
2222

2323
self.running.store(true, Ordering::Release);
2424

@@ -31,7 +31,11 @@ impl ManagedServer {
3131
continue;
3232
}
3333

34-
let session = match listener.accept_with_config(session_id, accept_cfg) {
34+
self.remember_windows_listener_handle(&listener);
35+
let accepted = listener.accept_with_config(session_id, accept_cfg);
36+
self.remember_windows_listener_handle(&listener);
37+
38+
let session = match accepted {
3539
Ok(s) => s,
3640
Err(_) => {
3741
if let Some(mut prepared) = prepared_shm {
@@ -82,13 +86,25 @@ impl ManagedServer {
8286
session_threads.push(t);
8387
}
8488

89+
*self.listener_handle.lock().unwrap() = None;
90+
8591
for t in session_threads {
8692
let _ = t.join();
8793
}
8894

8995
Ok(())
9096
}
9197

98+
fn remember_windows_listener_handle(&self, listener: &NpListener) {
99+
let handle = listener.handle();
100+
let stored = if handle == 0 || handle == -1 {
101+
None
102+
} else {
103+
Some(handle as usize)
104+
};
105+
*self.listener_handle.lock().unwrap() = stored;
106+
}
107+
92108
fn prepare_windows_accept(&mut self) -> (u64, ServerConfig, Option<PreparedWinShm>, bool) {
93109
let session_id = self.next_session_id;
94110
self.next_session_id += 1;

src/go/go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
module github.com/netdata/plugin-ipc/go
22

3-
go 1.25.11
3+
go 1.26.0

0 commit comments

Comments
 (0)