nx_jpegmem_probe.nx source
↩ module page · 95 lines · 3766 B
1// nx_jpegmem_probe.nx -- INSTRUMENT: does nx_jpeg_decode_rgb overrun/leak per call?
2// Decodes the SAME jpeg N times in one process, printing VmRSS after each decode, freeing the
3// caller-visible RGB each round. A flat RSS = the lib cleans up; a climbing RSS = per-call arena
4// growth (the OOM suspect for the 12-decode toon run); ARENA-OVERRUN lines localize the write.
5// usage: nx_jpegmem_probe <file.jpg> <rounds>
6
7import "nx_syscalls.nx"
8import "nx_jpeg_ascii.nx"
9
10func jp_slen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n }
11func jp_out(s: *u8) -> i64 { sys_write(1, s, jp_slen(s)); return 0 }
12func jp_outn(v: i64) -> i64 {
13 let b: *u8 = sys_mmap(32)
14 var x: i64 = v
15 var n: i64 = 0
16 if x == 0 { b[0] = 48 as u8; n = 1 } else {
17 while x > 0 { b[n] = (48 + (x % 10)) as u8; x = x / 10; n = n + 1 }
18 }
19 var i: i64 = 0
20 while i < n / 2 { let t: u8 = b[i]; b[i] = b[n-1-i]; b[n-1-i] = t; i = i + 1 }
21 sys_write(1, b, n)
22 return 0
23}
24
25func jp_rss() -> i64 {
26 let lp: *i64 = sys_mmap(16) as *i64
27 let b: *u8 = sys_read_file("/proc/self/status" as *u8, lp)
28 let n: i64 = lp[0]
29 if (b as i64) == 0 { return 0 - 1 }
30 var i: i64 = 0
31 while i + 6 < n {
32 if b[i] == (86 as u8) { if b[i+1] == (109 as u8) { if b[i+2] == (82 as u8) {
33 if b[i+3] == (83 as u8) { if b[i+4] == (83 as u8) {
34 var j: i64 = i + 5
35 var v: i64 = 0
36 var seen: i64 = 0
37 var run: i64 = 1
38 while run == 1 {
39 if j >= n { run = 0 } else {
40 let c: i64 = b[j] as i64
41 if c >= 48 { if c <= 57 { v = v * 10 + (c - 48); seen = 1; j = j + 1 } else {
42 if seen == 1 { run = 0 } else { j = j + 1 }
43 if c == 10 { run = 0 }
44 } } else {
45 if seen == 1 { run = 0 } else { j = j + 1 }
46 if c == 10 { run = 0 }
47 }
48 }
49 }
50 return v
51 } } } } }
52 i = i + 1
53 }
54 return 0 - 2
55}
56
57func jp_atoi(s: *u8) -> i64 {
58 var v: i64 = 0
59 var i: i64 = 0
60 while s[i] != (0 as u8) {
61 let c: i64 = s[i] as i64
62 if c >= 48 { if c <= 57 { v = v * 10 + (c - 48) } }
63 i = i + 1
64 }
65 return v
66}
67
68func main(argc: i64, argv: *i64) -> i64 {
69 if argc < 3 { jp_out("usage: nx_jpegmem_probe <file.jpg> <rounds>\n" as *u8); return 2 }
70 let lp: *i64 = sys_mmap(16) as *i64
71 let f: *u8 = sys_read_file(argv[1] as *u8, lp)
72 let flen: i64 = lp[0]
73 if (f as i64) == 0 { jp_out("cannot read input\n" as *u8); return 1 }
74 let rounds: i64 = jp_atoi(argv[2] as *u8)
75 jp_out("rss_before_kb=" as *u8); jp_outn(jp_rss()); jp_out("\n" as *u8)
76 var r: i64 = 0
77 while r < rounds {
78 let pr: *i64 = sys_mmap(8) as *i64
79 let pw: *i64 = sys_mmap(8) as *i64
80 let ph: *i64 = sys_mmap(8) as *i64
81 let rc: i64 = nx_jpeg_decode_rgb(f, flen, pr, pw, ph)
82 jp_out("round=" as *u8); jp_outn(r)
83 jp_out(" rc=" as *u8); jp_outn(rc)
84 jp_out(" w=" as *u8); jp_outn(pw[0])
85 jp_out(" h=" as *u8); jp_outn(ph[0])
86 // free with the EXACT allocated length (the lib mmaps width*height*3+16): the previous +64
87 // over-length munmap could clip the start of a neighbouring mapping when the allocation
88 // ended within 48 B of a page boundary -- a real defect, fixed 2026-08-23.
89 if rc == NX_JPEG_ASCII_OK { sys_munmap(pr[0] as *u8, pw[0] * ph[0] * 3 + 16) }
90 jp_out(" rss_kb=" as *u8); jp_outn(jp_rss())
91 jp_out("\n" as *u8)
92 r = r + 1
93 }
94 return 0
95}