@ai-ecoverse/slicc-kernel 1.179.0 → 1.180.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -7451,6 +7451,82 @@ var KERNEL_SIGINFO = Object.freeze({
7451
7451
  status: 0
7452
7452
  });
7453
7453
 
7454
+ // src/kernel/trace.ts
7455
+ var TRACE_LIMIT = 4096;
7456
+ var TRACE_LIMIT_MAX = 65536;
7457
+ function traceLimit(value2) {
7458
+ if (value2 === void 0) return TRACE_LIMIT;
7459
+ if (!Number.isInteger(value2) || value2 < 1 || value2 > TRACE_LIMIT_MAX) {
7460
+ throw Object.assign(new Error(`a trace limit is an integer from 1 to ${TRACE_LIMIT_MAX}`), {
7461
+ code: "EINVAL"
7462
+ });
7463
+ }
7464
+ return value2;
7465
+ }
7466
+ var TraceHub = class {
7467
+ subs = /* @__PURE__ */ new Set();
7468
+ seq = 0;
7469
+ get idle() {
7470
+ return this.subs.size === 0;
7471
+ }
7472
+ subscribe(tag, viewer, deliver, limit = TRACE_LIMIT) {
7473
+ const sub = {
7474
+ tag,
7475
+ viewer: copyCred(viewer),
7476
+ limit,
7477
+ queue: [],
7478
+ dropped: 0,
7479
+ waiting: false,
7480
+ scheduled: false,
7481
+ deliver
7482
+ };
7483
+ this.subs.add(sub);
7484
+ return {
7485
+ ack: () => {
7486
+ sub.waiting = false;
7487
+ this.schedule(sub);
7488
+ },
7489
+ close: () => {
7490
+ this.subs.delete(sub);
7491
+ sub.queue = [];
7492
+ }
7493
+ };
7494
+ }
7495
+ wants(tags, cred) {
7496
+ if (!tags?.length || this.subs.size === 0) return false;
7497
+ for (const sub of this.subs) if (this.matches(sub, tags, cred)) return true;
7498
+ return false;
7499
+ }
7500
+ publish(tags, cred, draft) {
7501
+ if (!tags?.length) return;
7502
+ let event;
7503
+ for (const sub of this.subs) {
7504
+ if (!this.matches(sub, tags, cred)) continue;
7505
+ event ??= { ...draft, seq: ++this.seq };
7506
+ if (sub.queue.length >= sub.limit) sub.dropped++;
7507
+ else sub.queue.push(event);
7508
+ this.schedule(sub);
7509
+ }
7510
+ }
7511
+ matches(sub, tags, cred) {
7512
+ return tags.includes(sub.tag) && maySee(sub.viewer, cred);
7513
+ }
7514
+ schedule(sub) {
7515
+ if (sub.scheduled || sub.waiting) return;
7516
+ if (sub.queue.length === 0 && sub.dropped === 0) return;
7517
+ sub.scheduled = true;
7518
+ queueMicrotask(() => {
7519
+ sub.scheduled = false;
7520
+ if (!this.subs.has(sub)) return;
7521
+ const batch = { events: sub.queue, dropped: sub.dropped };
7522
+ sub.queue = [];
7523
+ sub.dropped = 0;
7524
+ sub.waiting = true;
7525
+ sub.deliver(batch);
7526
+ });
7527
+ }
7528
+ };
7529
+
7454
7530
  // src/body-idle.ts
7455
7531
  var BODY_IDLE_MS = 5 * 60 * 1e3;
7456
7532
 
@@ -8130,7 +8206,7 @@ var UserDb = class {
8130
8206
  };
8131
8207
 
8132
8208
  // src/client/protocol.ts
8133
- var PROTOCOL = [1, 14];
8209
+ var PROTOCOL = [1, 15];
8134
8210
  var FS_METHODS = [
8135
8211
  "readFile",
8136
8212
  "writeFile",
@@ -8297,9 +8373,21 @@ async function runAs(l, self, requested) {
8297
8373
  function clientCred(self) {
8298
8374
  return self ? credOf(self) : copyCred(ROOT);
8299
8375
  }
8376
+ var MAX_SPAWN_TAGS = 8;
8377
+ function spawnTags(value2) {
8378
+ if (!Array.isArray(value2)) {
8379
+ tagOption(value2);
8380
+ return value2 === void 0 ? [] : [value2];
8381
+ }
8382
+ if (value2.length > MAX_SPAWN_TAGS) {
8383
+ throw new TypeError(`a spawn adds at most ${MAX_SPAWN_TAGS} tags`);
8384
+ }
8385
+ for (const each of value2) tagOption(each ?? 0);
8386
+ return value2;
8387
+ }
8300
8388
  async function runTagged(l, self, options, tag, session = -1) {
8301
- tagOption(options.tag);
8302
- const tags = withTag(withTag(void 0, tag), options.tag);
8389
+ let tags = withTag(void 0, tag);
8390
+ for (const added of spawnTags(options.tag)) tags = withTag(tags, added);
8303
8391
  return {
8304
8392
  user: await runAs(l, self, options.user),
8305
8393
  tag,
@@ -8430,6 +8518,34 @@ async function watchOp(l, req, watches, send2, g) {
8430
8518
  watches.set(req.id, unwatch);
8431
8519
  return { result: req.id };
8432
8520
  }
8521
+ function traceRegistry(send2) {
8522
+ const traces = /* @__PURE__ */ new Map();
8523
+ return {
8524
+ answer(l, self, req) {
8525
+ if (req.op === "untrace") {
8526
+ traces.get(req.trace)?.close();
8527
+ return { result: traces.delete(req.trace) };
8528
+ }
8529
+ if (typeof req.tag !== "string" || req.tag === "") {
8530
+ throw fsError("EINVAL", "trace: a tag is a non-empty string");
8531
+ }
8532
+ const limit = traceLimit(req.limit);
8533
+ const id2 = req.id;
8534
+ const deliver = ({ events, dropped: dropped2 }) => send2({ trace: id2, events, ...dropped2 > 0 ? { dropped: dropped2 } : {} });
8535
+ traces.set(id2, l.traces.subscribe(req.tag, credOf(self), deliver, limit));
8536
+ return { result: id2 };
8537
+ },
8538
+ ack(data) {
8539
+ if (typeof data.traceAck !== "number") return false;
8540
+ traces.get(data.traceAck)?.ack();
8541
+ return true;
8542
+ },
8543
+ close() {
8544
+ for (const trace of traces.values()) trace.close();
8545
+ traces.clear();
8546
+ }
8547
+ };
8548
+ }
8433
8549
  var TRAITS_WATCH = -1;
8434
8550
  function traitsWatch(launcher, transport, send2) {
8435
8551
  return launcher.onTraits(() => send2({ traits: transport.traits }));
@@ -8456,6 +8572,21 @@ function requestBinding(send2, port) {
8456
8572
  close: () => unbind?.()
8457
8573
  };
8458
8574
  }
8575
+ function answered(pending, req, reply, detach) {
8576
+ pending.then(
8577
+ ({ result, signal, transfer }) => {
8578
+ reply(req.id, { result, ...signal ? { signal } : {} }, transfer);
8579
+ if (req.op === "detach") detach(req.kill === true);
8580
+ },
8581
+ (err) => {
8582
+ const code = err?.code;
8583
+ reply(req.id, {
8584
+ error: err instanceof Error ? err.message : String(err),
8585
+ ...typeof code === "string" ? { code } : {}
8586
+ });
8587
+ }
8588
+ );
8589
+ }
8459
8590
  function serveClient(port, host) {
8460
8591
  let state = "new";
8461
8592
  let self;
@@ -8471,6 +8602,7 @@ function serveClient(port, host) {
8471
8602
  };
8472
8603
  const reply = (id2, body, transfer) => send2({ id: id2, ...body }, transfer);
8473
8604
  const hosts = hostRegistries(send2, ended.promise, port);
8605
+ const traces = traceRegistry(send2);
8474
8606
  function detach(kill = false, bye = "the slicc-kernel detached this client") {
8475
8607
  if (state === "closed") return;
8476
8608
  send2({ bye });
@@ -8478,6 +8610,7 @@ function serveClient(port, host) {
8478
8610
  net?.close();
8479
8611
  for (const unwatch of watches.values()) unwatch();
8480
8612
  watches.clear();
8613
+ traces.close();
8481
8614
  hosts.close();
8482
8615
  for (const session of terminals.values()) session.close();
8483
8616
  terminals.clear();
@@ -8585,6 +8718,9 @@ function serveClient(port, host) {
8585
8718
  case "serve-facade":
8586
8719
  case "unserve-facade":
8587
8720
  return { result: hosts.answer(l, req) };
8721
+ case "trace":
8722
+ case "untrace":
8723
+ return traces.answer(l, self, req);
8588
8724
  case "detach":
8589
8725
  return { result: true };
8590
8726
  default:
@@ -8608,20 +8744,9 @@ function serveClient(port, host) {
8608
8744
  net?.answer(data);
8609
8745
  return;
8610
8746
  }
8747
+ if (traces.ack(data)) return;
8611
8748
  const req = data;
8612
- handle(req).then(
8613
- ({ result, signal, transfer }) => {
8614
- reply(req.id, { result, ...signal ? { signal } : {} }, transfer);
8615
- if (req.op === "detach") detach(req.kill === true);
8616
- },
8617
- (err) => {
8618
- const code = err?.code;
8619
- reply(req.id, {
8620
- error: err instanceof Error ? err.message : String(err),
8621
- ...typeof code === "string" ? { code } : {}
8622
- });
8623
- }
8624
- );
8749
+ answered(handle(req), req, reply, detach);
8625
8750
  });
8626
8751
  port.addEventListener("close", () => detach());
8627
8752
  port.start?.();
@@ -8779,7 +8904,7 @@ function satisfies(version, range2) {
8779
8904
  }
8780
8905
 
8781
8906
  // src/version.ts
8782
- var KERNEL_VERSION = true ? "1.179.0" : "0.0.0-dev";
8907
+ var KERNEL_VERSION = true ? "1.180.0" : "0.0.0-dev";
8783
8908
  var OS_RELEASE = "6.0.0-slicc";
8784
8909
  function procVersion() {
8785
8910
  return `Linux version ${OS_RELEASE} (slicc-kernel ${KERNEL_VERSION}) #1 SMP PREEMPT_DYNAMIC
@@ -30069,6 +30194,9 @@ var JobTable = class {
30069
30194
  idOf(pid) {
30070
30195
  return this.members.get(pid)?.shownAs ?? pid;
30071
30196
  }
30197
+ execParentOf(pid) {
30198
+ return this.members.get(pid)?.execParent;
30199
+ }
30072
30200
  byId(id2) {
30073
30201
  for (const member of this.members.values()) if (member.shownAs === id2) return member;
30074
30202
  return this.member(id2);
@@ -35283,10 +35411,12 @@ var Launcher = class {
35283
35411
  });
35284
35412
  const fit = await (handle.ready ?? true);
35285
35413
  if (fit) {
35414
+ this.traceAdmitted(handle.pid, true);
35286
35415
  await fds.closeAll();
35287
35416
  return handle;
35288
35417
  }
35289
35418
  await handle.exited;
35419
+ this.traceAdmitted(handle.pid, false);
35290
35420
  await this.memories.refused().catch(async (err) => {
35291
35421
  await fds.closeAll();
35292
35422
  throw err;
@@ -35417,10 +35547,12 @@ var Launcher = class {
35417
35547
  if (req.group === "lead") this.jobs.lead(pid);
35418
35548
  if (req.group === "session") this.jobs.setsid(pid);
35419
35549
  this.described.set(pid, describe(req, terminal?.name ?? null));
35550
+ this.traceStarted(pid, req);
35420
35551
  handle.onState(
35421
35552
  (state) => state === "stopped" ? this.stopped.add(pid) : this.stopped.delete(pid)
35422
35553
  );
35423
- void handle.exited.then(() => {
35554
+ void handle.exited.then((status) => {
35555
+ this.traceEnded(pid, handle, status);
35424
35556
  this.cwds.release(this.cwdSlots.get(pid));
35425
35557
  this.cwdSlots.delete(pid);
35426
35558
  this.cwdPaths.delete(pid);
@@ -35535,6 +35667,71 @@ var Launcher = class {
35535
35667
  imageTimes = /* @__PURE__ */ new Map();
35536
35668
  childTimes = /* @__PURE__ */ new Map();
35537
35669
  stopped = /* @__PURE__ */ new Set();
35670
+ traces = new TraceHub();
35671
+ traceHeld = /* @__PURE__ */ new Map();
35672
+ replacedImages = /* @__PURE__ */ new Set();
35673
+ traceStarted(pid, req) {
35674
+ const replaces = req.exec ? req.ppid : void 0;
35675
+ if (req.quietOom) this.traceHeld.set(pid, { replaces, events: [] });
35676
+ else if (replaces !== void 0) this.replacedImages.add(replaces);
35677
+ this.traceEmit(pid, () => {
35678
+ const { pid: shown2, ppid } = this.identityOf(pid);
35679
+ const described2 = this.described.get(pid);
35680
+ const { argv, started, tty: _tty, exeId: _exeId, ...exe } = described2;
35681
+ return {
35682
+ type: req.fork ? "fork" : "exec",
35683
+ pid: shown2,
35684
+ ppid,
35685
+ pgid: this.jobs.pgidOf(pid),
35686
+ argv: [...argv],
35687
+ ...exe,
35688
+ uid: this.credOf(pid).euid,
35689
+ at: started,
35690
+ ...req.exec ? { replaced: true } : {}
35691
+ };
35692
+ });
35693
+ }
35694
+ traceEnded(pid, handle, status) {
35695
+ if (this.replacedImages.delete(pid)) return;
35696
+ this.traceEmit(pid, () => ({
35697
+ type: "exit",
35698
+ pid: this.jobs.idOf(pid),
35699
+ status,
35700
+ ...signalField(handle.termsig()),
35701
+ ...this.chainTimes(pid),
35702
+ at: Date.now()
35703
+ }));
35704
+ }
35705
+ traceEmit(pid, build) {
35706
+ if (this.traces.idle) return;
35707
+ const tags = this.tagsOf(pid);
35708
+ const cred = this.credOf(pid);
35709
+ if (!this.traces.wants(tags, cred)) return;
35710
+ const held = this.traceHeld.get(pid);
35711
+ if (held) held.events.push({ tags, cred: copyCred(cred), draft: build() });
35712
+ else this.traces.publish(tags, cred, build());
35713
+ }
35714
+ traceAdmitted(pid, fit) {
35715
+ const held = this.traceHeld.get(pid);
35716
+ if (!held) return;
35717
+ this.traceHeld.delete(pid);
35718
+ if (!fit) return;
35719
+ if (held.replaces !== void 0) this.replacedImages.add(held.replaces);
35720
+ for (const { tags, cred, draft } of held.events) this.traces.publish(tags, cred, draft);
35721
+ }
35722
+ chainTimes(pid) {
35723
+ let utime = 0;
35724
+ let stime = 0;
35725
+ let timed2 = false;
35726
+ for (let at = pid; at !== void 0; at = this.jobs.execParentOf(at)) {
35727
+ const t = this.timesAt(at);
35728
+ if (!t) continue;
35729
+ timed2 = true;
35730
+ utime += t.utime;
35731
+ stime += t.stime;
35732
+ }
35733
+ return timed2 ? { utime, stime } : {};
35734
+ }
35538
35735
  stateOf(pid) {
35539
35736
  if (!this.processes.has(pid)) return "Z";
35540
35737
  return this.stopped.has(pid) ? "T" : "S";
@@ -37061,7 +37258,7 @@ function sudoApprovals(port) {
37061
37258
  }
37062
37259
  };
37063
37260
  }
37064
- function answered(data, remote, uplink, approvals) {
37261
+ function answered2(data, remote, uplink, approvals) {
37065
37262
  if (data.sudoAnswer) approvals.answer(data.sudoAnswer);
37066
37263
  else if (data.net !== void 0) remote?.receive(data);
37067
37264
  else if (typeof data.uplink === "string") uplink?.receive(data);
@@ -37346,7 +37543,7 @@ function serveKernel(port, deps) {
37346
37543
  const grant = event.data.hostfsGrant;
37347
37544
  if (grant) return granted2(grant);
37348
37545
  if (allowed(event.data)) return;
37349
- if (answered(event.data, remote, uplink, approvals)) return;
37546
+ if (answered2(event.data, remote, uplink, approvals)) return;
37350
37547
  const req = event.data;
37351
37548
  handle(req).then(
37352
37549
  (result) => reply(req.id, { result }, transferOf2(req, result)),