╭─────────────╮ │scroll to top│ ╰─────────────╯ ╭ IncrementalDebugServer.zig ───────────────────────────────────╮ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ╰───────────────────────────────────────────────────────────────╯
╭ find file ─ ─ ─ ─ ─ ─ ─ ─ ─ ─╮ ╭────╮ ╭ clone ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ╮ │ │ │http│ ssh │ │ ╰ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─╯ ╰────╯ ╰ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ╯
119 EndOfStream, 120 StreamTooLong, 121 ReadFailed, 122 WriteFailed, 123 }!noreturn { 124 const gpa = ids.zcu.gpa; 125 const io = ids.zcu.comp.io; 126 127 var text_out: std.ArrayList(u8) = .empty; 128 defer text_out.deinit(gpa); 129 130 while (true) { 131 try stream_writer.writeAll("zig> "); 132 const untrimmed = try stream_reader.takeSentinel('\n'); 133 const cmd_and_arg = std.mem.trim(u8, untrimmed, " \t\r\n"); 134 const cmd: []const u8, const arg: []const u8 = if (std.mem.findScalar(u8, cmd_and_arg, ' ')) |i| 135 .{ cmd_and_arg[0..i], cmd_and_arg[i + 1 ..] } 136 else 137 .{ cmd_and_arg, "" }; 138 139 text_out.clearRetainingCapacity(); 140 { 141 if (!ids.mutex.tryLock()) { 142 try stream_writer.writeAll("waiting for in-progress update to finish...\n"); 143 try ids.mutex.lock(io); 144 } 145 defer ids.mutex.unlock(io); 146 var allocating: Io.Writer.Allocating = .fromArrayList(gpa, &text_out); 147 defer text_out = allocating.toArrayList(); 148 handleCommand(ids.zcu, &allocating.writer, cmd, arg) catch |err| switch (err) { 149 error.OutOfMemory, 150 error.WriteFailed, 151 => return error.OutOfMemory, 152 }; 153 } 154 try text_out.append(gpa, '\n'); 155 try stream_writer.writeAll(text_out.items); 156 } 157 } 158 159 const help_str: []const u8 = 160 \\[str] arguments are any string. 161 \\[id] arguments are a numeric ID/index, like an InternPool index. 162 \\[unit] arguments are strings like 'func 1234' where '1234' is the relevant index (in this case an InternPool index). 163 \\ 164 \\MISC 165 \\ summary 166 \\ Dump some information about the whole ZCU. 167 \\ nav_info [id] 168 \\ Dump basic info about a NAV. 169 \\ 170 \\SEARCHING 171 \\ find_type [str] 172 \\ Find types (including dead ones) whose names contain the given substring. 173 \\ Starting with '^' or ending with '$' anchors to the start/end of the name. 174 \\ find_nav [str] 175 \\ Find NAVs (including dead ones) whose names contain the given substring. 176 \\ Starting with '^' or ending with '$' anchors to the start/end of the name. 177 \\ 178 \\UNITS 179 \\ unit_info [unit] 180 \\ Dump basic info about an analysis unit. 181 \\ unit_dependencies [unit] 182 \\ List all units which an analysis unit depends on. 183 \\ unit_trace [unit] 184 \\ Dump the current reference trace of an analysis unit. 185 \\ 186 \\TYPES 187 \\ type_info [id] 188 \\ Dump basic info about a type. 189 \\ type_namespace [id] 190 \\ List all declarations in the namespace of a type. 191 \\ 192 ; 193 194 fn handleCommand(zcu: *Zcu, w: *Io.Writer, cmd_str: []const u8, arg_str: []const u8) error{ WriteFailed, OutOfMemory }!void { 195 const ip = &zcu.intern_pool; 196 if (std.mem.eql(u8, cmd_str, "help")) { 197 try w.writeAll(help_str); 198 } else if (std.mem.eql(u8, cmd_str, "summary")) { 199 try w.print( 200 \\last generation: {d} 201 \\total container types: {d} 202 \\total NAVs: {d} 203 \\total units: {d} 204 \\ 205 , .{ 206 zcu.generation - 1, 207 zcu.incremental_debug_state.types.count(), 208 zcu.incremental_debug_state.navs.count(), 209 zcu.incremental_debug_state.units.count(), 210 }); 211 } else if (std.mem.eql(u8, cmd_str, "nav_info")) { 212 const nav_index: InternPool.Nav.Index = @fromBackingInt(@intCast(parseIndex(arg_str) orelse return w.writeAll("malformed nav index"))); 213 const create_gen = zcu.incremental_debug_state.navs.get(nav_index) orelse return w.writeAll("unknown nav index"); 214 const nav = ip.getNav(nav_index); 215 try w.print( 216 \\name: '{f}' 217 \\fqn: '{f}' 218 \\created on generation: {d} 219 \\ 220 , .{ 221 nav.name.fmt(ip), 222 nav.fqn.fmt(ip), 223 create_gen, 224 }); 225 if (nav.resolved) |r| { 226 try w.writeAll("status: resolved\n type: "); 227 try printType(.fromInterned(r.type), zcu, w); 228 try w.writeAll("\n value: "); 229 if (r.value == .none) { 230 try w.writeAll("(unresolved)"); 231 } else { 232 try printType(.fromInterned(r.type), zcu, w); 233 } 234 try w.writeByte('\n'); 235 } else { 236 try w.writeAll("status: unresolved\n"); 237 } 238 } else if (std.mem.eql(u8, cmd_str, "find_type")) { 239 if (arg_str.len == 0) return w.writeAll("bad usage"); 240 const anchor_start = arg_str[0] == '^'; 241 const anchor_end = arg_str[arg_str.len - 1] == '$'; 242 const query = arg_str[@intFromBool(anchor_start) .. arg_str.len - @intFromBool(anchor_end)]; 243 var num_results: usize = 0; 244 for (zcu.incremental_debug_state.types.keys()) |type_ip_index| { 245 const ty: Type = .fromInterned(type_ip_index); 246 const ty_name = ty.containerTypeName(ip).fqn.toSlice(ip); 247 const success = switch (@as(u2, @intFromBool(anchor_start)) << 1 | @intFromBool(anchor_end)) { 248 0b00 => std.mem.find(u8, ty_name, query) != null, 249 0b01 => std.mem.endsWith(u8, ty_name, query), 250 0b10 => std.mem.startsWith(u8, ty_name, query), 251 0b11 => std.mem.eql(u8, ty_name, query), 252 }; 253 if (success) { 254 num_results += 1; 255 try w.print("* type {d} ('{s}')\n", .{ @backingInt(type_ip_index), ty_name }); 256 } 257 } 258 try w.print("Found {d} results\n", .{num_results}); 259 } else if (std.mem.eql(u8, cmd_str, "find_nav")) { 260 if (arg_str.len == 0) return w.writeAll("bad usage"); 261 const anchor_start = arg_str[0] == '^'; 262 const anchor_end = arg_str[arg_str.len - 1] == '$'; 263 const query = arg_str[@intFromBool(anchor_start) .. arg_str.len - @intFromBool(anchor_end)]; 264 var num_results: usize = 0; 265 for (zcu.incremental_debug_state.navs.keys()) |nav_index| { 266 const nav = ip.getNav(nav_index); 267 const nav_fqn = nav.fqn.toSlice(ip); 268 const success = switch (@as(u2, @intFromBool(anchor_start)) << 1 | @intFromBool(anchor_end)) { 269 0b00 => std.mem.find(u8, nav_fqn, query) != null, 270 0b01 => std.mem.endsWith(u8, nav_fqn, query), 271 0b10 => std.mem.startsWith(u8, nav_fqn, query), 272 0b11 => std.mem.eql(u8, nav_fqn, query), 273 }; 274 if (success) { 275 num_results += 1; 276 try w.print("* nav {d} ('{s}')\n", .{ @backingInt(nav_index), nav_fqn }); 277 } 278 } 279 try w.print("Found {d} results\n", .{num_results}); 280 } else if (std.mem.eql(u8, cmd_str, "unit_info")) { 281 const unit = parseAnalUnit(arg_str) orelse return w.writeAll("malformed anal unit"); 282 const unit_info = zcu.incremental_debug_state.units.get(unit) orelse return w.writeAll("unknown anal unit"); 283 var ref_str_buf: [32]u8 = undefined; 284 const ref_str: []const u8 = ref: { 285 const refs = try zcu.resolveReferences(); 286 const ref = refs.get(unit) orelse break :ref "<unreferenced>"; 287 const referencer = (ref orelse break :ref "<analysis root>").referencer; 288 break :ref printAnalUnit(referencer, &ref_str_buf); 289 }; 290 try w.print( 291 \\last update generation: {d} 292 \\current referencer: {s} 293 \\ 294 , .{ 295 unit_info.last_update_gen, 296 ref_str, 297 }); 298 if (zcu.failed_analysis.get(unit)) |err_msg| { 299 try w.print("analysis result: failure ({q})\n", .{err_msg.msg}); 300 } else if (zcu.transitive_failed_analysis.get(unit)) |reason| { 301 switch (reason) { 302 .astgen_error => try w.writeAll("analysis result: transitive failure (astgen error)\n"), 303 .dependency_loop => try w.writeAll("analysis result: transitive failure (dependency loop)\n"), 304 .lost_tracking => try w.writeAll("analysis result: transitive failure (lost tracking for zir inst)\n"), 305 .failed_unit => |other_unit| { 306 var buf: [32]u8 = undefined; 307 try w.print("analysis result: transitive failure (failed unit: {s})\n", .{printAnalUnit(other_unit, &buf)}); 308 }, 309 .func_nav_val_changed => |func_index| try w.print("analysis result: transitive failure (owner nav of func '{d}' changed value)\n", .{@backingInt(func_index)}), 310 } 311 } else { 312 try w.writeAll("analysis result: success\n"); 313 } 314 if (unit.unwrap() == .func) { 315 const nav_id = zcu.intern_pool.indexToKey(unit.unwrap().func).func.owner_nav; 316 try w.print("owner nav: {d}\n", .{@backingInt(nav_id)}); 317 } 318 } else if (std.mem.eql(u8, cmd_str, "unit_dependencies")) { 319 const unit = parseAnalUnit(arg_str) orelse return w.writeAll("malformed anal unit"); 320 const unit_info = zcu.incremental_debug_state.units.get(unit) orelse return w.writeAll("unknown anal unit"); 321 for (unit_info.deps.items, 0..) |dependee, i| { 322 try w.print("[{d}] ", .{i}); 323 switch (dependee) { 324 .src_hash, .namespace, .namespace_name, .source_file, .embed_file => try w.print("{f}", .{zcu.fmtDependee(dependee)}), 325 .nav_val, .nav_ty => |nav| try w.print("{t} {d}", .{ dependee, @backingInt(nav) }), 326 .type_layout, .struct_defaults, .func_ies => |ip_index| try w.print("{t} {d}", .{ dependee, @backingInt(ip_index) }), 327 .memoized_state => |stage| try w.print("memoized_state {s}", .{@tagName(stage)}), 328 } 329 try w.writeByte('\n'); 330 } 331 } else if (std.mem.eql(u8, cmd_str, "unit_trace")) { 332 const unit = parseAnalUnit(arg_str) orelse return w.writeAll("malformed anal unit"); 333 if (!zcu.incremental_debug_state.units.contains(unit)) return w.writeAll("unknown anal unit"); 334 const refs = try zcu.resolveReferences(); 335 if (!refs.contains(unit)) return w.writeAll("not referenced"); 336 var opt_cur: ?AnalUnit = unit; 337 while (opt_cur) |cur| { 338 var buf: [32]u8 = undefined; 339 try w.print("* {s}\n", .{printAnalUnit(cur, &buf)}); 340 opt_cur = if (refs.get(cur).?) |ref| ref.referencer else null; 341 } 342 } else if (std.mem.eql(u8, cmd_str, "type_info")) { 343 const ip_index: InternPool.Index = @fromBackingInt(@intCast(parseIndex(arg_str) orelse return w.writeAll("malformed ip index"))); 344 const create_gen = zcu.incremental_debug_state.types.get(ip_index) orelse return w.writeAll("unknown type"); 345 try w.print( 346 \\name: '{f}' 347 \\created on generation: {d} 348 \\ 349 , .{ 350 Type.fromInterned(ip_index).containerTypeName(ip).fqn.fmt(ip), 351 create_gen, 352 }); 353 } else if (std.mem.eql(u8, cmd_str, "type_namespace")) { 354 const ip_index: InternPool.Index = @fromBackingInt(@intCast(parseIndex(arg_str) orelse return w.writeAll("malformed ip index"))); 355 if (!zcu.incremental_debug_state.types.contains(ip_index)) return w.writeAll("unknown type"); 356 const ns = zcu.namespacePtr(Type.fromInterned(ip_index).getNamespaceIndex(zcu)); 357 try w.print("{d} pub decls:\n", .{ns.pub_decls.count()}); 358 for (ns.pub_decls.keys()) |nav| { 359 try w.print("* nav {d}\n", .{@backingInt(nav)}); 360 } 361 try w.print("{d} non-pub decls:\n", .{ns.priv_decls.count()}); 362 for (ns.priv_decls.keys()) |nav| { 363 try w.print("* nav {d}\n", .{@backingInt(nav)}); 364 } 365 try w.print("{d} comptime decls:\n", .{ns.comptime_decls.items.len}); 366 for (ns.comptime_decls.items) |id| { 367 try w.print("* comptime {d}\n", .{@backingInt(id)}); 368 } 369 try w.print("{d} tests:\n", .{ns.test_decls.items.len}); 370 for (ns.test_decls.items) |nav| { 371 try w.print("* nav {d}\n", .{@backingInt(nav)}); 372 } 373 } else { 374 try w.writeAll("command not found; run 'help' for a command list"); 375 } 376 } 377 378 fn parseIndex(str: []const u8) ?u32 { 379 return std.fmt.parseInt(u32, str, 10) catch null; 380 } 381 fn parseAnalUnit(str: []const u8) ?AnalUnit { 382 const split_idx = std.mem.findScalar(u8, str, ' ') orelse return null; 383 const kind = str[0..split_idx]; 384 const idx_str = str[split_idx + 1 ..]; 385 if (std.mem.eql(u8, kind, "comptime")) { 386 return .wrap(.{ .@"comptime" = @fromBackingInt(@intCast(parseIndex(idx_str) orelse return null)) }); 387 } else if (std.mem.eql(u8, kind, "nav_val")) { 388 return .wrap(.{ .nav_val = @fromBackingInt(@intCast(parseIndex(idx_str) orelse return null)) }); 389 } else if (std.mem.eql(u8, kind, "nav_ty")) { 390 return .wrap(.{ .nav_ty = @fromBackingInt(@intCast(parseIndex(idx_str) orelse return null)) }); 391 } else if (std.mem.eql(u8, kind, "type_layout")) { 392 return .wrap(.{ .type_layout = @fromBackingInt(@intCast(parseIndex(idx_str) orelse return null)) }); 393 } else if (std.mem.eql(u8, kind, "struct_defaults")) { 394 return .wrap(.{ .struct_defaults = @fromBackingInt(@intCast(parseIndex(idx_str) orelse return null)) }); 395 } else if (std.mem.eql(u8, kind, "func")) { 396 return .wrap(.{ .func = @fromBackingInt(@intCast(parseIndex(idx_str) orelse return null)) }); 397 } else if (std.mem.eql(u8, kind, "memoized_state")) { 398 return .wrap(.{ .memoized_state = std.meta.stringToEnum( 399 InternPool.MemoizedStateStage, 400 idx_str, 401 ) orelse return null }); 402 } else { 403 return null; 404 } 405 } 406 fn printAnalUnit(unit: AnalUnit, buf: *[32]u8) []const u8 { 407 const idx: u32 = switch (unit.unwrap()) { 408 .memoized_state => |stage| return std.mem.print(buf, "memoized_state {s}", .{@tagName(stage)}) catch unreachable, 409 inline else => |i| @backingInt(i), 410 }; 411 return std.mem.print(buf, "{s} {d}", .{ @tagName(unit.unwrap()), idx }) catch unreachable; 412 } 413 414 fn printType(ty: Type, zcu: *const Zcu, w: *Io.Writer) Io.Writer.Error!void { 415 const ip = &zcu.intern_pool; 416 switch (ip.indexToKey(ty.toIntern())) { 417 .int_type => |int| try w.print("{c}{d}", .{ 418 @as(u8, if (int.signedness == .unsigned) 'u' else 'i'), 419 int.bits, 420 }), 421 .tuple_type => try w.writeAll("(tuple)"), 422 .error_set_type => try w.writeAll("(error set)"), 423 .inferred_error_set_type => try w.writeAll("(inferred error set)"), 424 .func_type => try w.writeAll("(function)"), 425 .anyframe_type => try w.writeAll("(anyframe)"), 426 .vector_type => { 427 try w.print("@Vector({d}, ", .{ty.vectorLen(zcu)}); 428 try printType(ty.childType(zcu), zcu, w); 429 try w.writeByte(')'); 430 }, 431 .array_type => { 432 try w.print("[{d}]", .{ty.arrayLen(zcu)}); 433 try printType(ty.childType(zcu), zcu, w); 434 }, 435 .opt_type => { 436 try w.writeByte('?'); 437 try printType(ty.optionalChild(zcu), zcu, w); 438 }, 439 .error_union_type => { 440 try printType(ty.errorUnionSet(zcu), zcu, w); 441 try w.writeByte('!'); 442 try printType(ty.errorUnionPayload(zcu), zcu, w); 443 }, 444 .ptr_type => { 445 try w.writeAll("*(attrs) "); 446 try printType(ty.childType(zcu), zcu, w); 447 }, 448 .simple_type => |simple| try w.writeAll(@tagName(simple)), 449 450 .struct_type, 451 .union_type, 452 .enum_type, 453 .opaque_type, 454 => try w.print("{f}[{d}]", .{ ty.containerTypeName(ip).fqn.fmt(ip), @backingInt(ty.toIntern()) }), 455 456 else => unreachable, 457 } 458 } 459 460 const std = @import("std"); 461 const Io = std.Io; 462 const Allocator = std.mem.Allocator; 463 const log = std.log.scoped(.incremental_debug_server); 464 465 const Compilation = @import("Compilation.zig"); 466 const Zcu = @import("Zcu.zig"); 467 const InternPool = @import("InternPool.zig"); 468 const Type = @import("Type.zig"); 469 const AnalUnit = InternPool.AnalUnit; 470 471 const IncrementalDebugServer = @This(); 472