From 29d2dd68025226835a0bc54544124ab63eb54960 Mon Sep 17 00:00:00 2001 From: Auguste Rame Date: Sun, 5 Jun 2022 23:50:17 -0400 Subject: [PATCH] Add tracy --- build.zig | 40 +++++ src/DocumentStore.zig | 7 + src/main.zig | 192 +++++++++++++++++++++++- src/tracy.zig | 333 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 567 insertions(+), 5 deletions(-) create mode 100644 src/tracy.zig diff --git a/build.zig b/build.zig index 37afeb0..df7ccd3 100644 --- a/build.zig +++ b/build.zig @@ -22,8 +22,48 @@ pub fn build(b: *std.build.Builder) !void { b.option(std.log.Level, "log_level", "The Log Level to be used.") orelse .info, ); + const enable_tracy = b.option(bool, "enable_tracy", "Whether of not tracy should be enabled.") orelse false; + + exe_options.addOption( + bool, + "enable_tracy", + enable_tracy, + ); + + exe_options.addOption( + bool, + "enable_tracy_allocation", + b.option(bool, "enable_tracy_allocation", "Enable using TracyAllocator to monitor allocations.") orelse false, + ); + + exe_options.addOption( + bool, + "enable_tracy_callstack", + b.option(bool, "enable_tracy_callstack", "Enable callstack graphs.") orelse false, + ); + exe.addPackage(.{ .name = "known-folders", .source = .{ .path = "src/known-folders/known-folders.zig" } }); + if (enable_tracy) { + const client_cpp = "src/tracy/TracyClient.cpp"; + + // On mingw, we need to opt into windows 7+ to get some features required by tracy. + const tracy_c_flags: []const []const u8 = if (target.isWindows() and target.getAbi() == .gnu) + &[_][]const u8{ "-DTRACY_ENABLE=1", "-fno-sanitize=undefined", "-D_WIN32_WINNT=0x601" } + else + &[_][]const u8{ "-DTRACY_ENABLE=1", "-fno-sanitize=undefined" }; + + exe.addIncludePath("src/tracy"); + exe.addCSourceFile(client_cpp, tracy_c_flags); + exe.linkSystemLibraryName("c++"); + exe.linkLibC(); + + if (target.isWindows()) { + exe.linkSystemLibrary("dbghelp"); + exe.linkSystemLibrary("ws2_32"); + } + } + exe.setTarget(target); exe.setBuildMode(mode); exe.install(); diff --git a/src/DocumentStore.zig b/src/DocumentStore.zig index 4eac867..bf4b662 100644 --- a/src/DocumentStore.zig +++ b/src/DocumentStore.zig @@ -6,6 +6,7 @@ const offsets = @import("./offsets.zig"); const log = std.log.scoped(.doc_store); const Ast = std.zig.Ast; const BuildAssociatedConfig = @import("./BuildAssociatedConfig.zig"); +const tracy = @import("./tracy.zig"); const DocumentStore = @This(); @@ -119,6 +120,9 @@ const LoadPackagesContext = struct { }; fn loadPackages(context: LoadPackagesContext) !void { + const tracy_zone = tracy.trace(@src()); + defer tracy_zone.end(); + const allocator = context.allocator; const build_file = context.build_file; const build_runner_path = context.build_runner_path; @@ -195,6 +199,9 @@ fn loadPackages(context: LoadPackagesContext) !void { /// This function asserts the document is not open yet and takes ownership /// of the uri and text passed in. fn newDocument(self: *DocumentStore, uri: []const u8, text: [:0]u8) anyerror!*Handle { + const tracy_zone = tracy.trace(@src()); + defer tracy_zone.end(); + log.debug("Opened document: {s}", .{uri}); var handle = try self.allocator.create(Handle); diff --git a/src/main.zig b/src/main.zig index 87760e8..8459099 100644 --- a/src/main.zig +++ b/src/main.zig @@ -16,6 +16,7 @@ const semantic_tokens = @import("./semantic_tokens.zig"); const shared = @import("./shared.zig"); const Ast = std.zig.Ast; const known_folders = @import("known-folders"); +const tracy = @import("./tracy.zig"); const data = switch (build_options.data_version) { .master => @import("data/master.zig"), @@ -38,6 +39,9 @@ var actual_log_level: std.log.Level = switch (zig_builtin.mode) { }; pub fn log(comptime message_level: std.log.Level, comptime scope: @Type(.EnumLiteral), comptime format: []const u8, args: anytype) void { + const tracy_zone = tracy.trace(@src()); + defer tracy_zone.end(); + if (@enumToInt(message_level) > @enumToInt(actual_log_level)) { return; } @@ -118,6 +122,9 @@ const no_semantic_tokens_response = /// Sends a request or response fn send(arena: *std.heap.ArenaAllocator, reqOrRes: anytype) !void { + const tracy_zone = tracy.trace(@src()); + defer tracy_zone.end(); + var arr = std.ArrayList(u8).init(arena.allocator()); try std.json.stringify(reqOrRes, .{}, arr.writer()); @@ -138,6 +145,9 @@ fn truncateCompletions(list: []types.CompletionItem, max_detail_length: usize) v } fn respondGeneric(id: types.RequestId, response: []const u8) !void { + const tracy_zone = tracy.trace(@src()); + defer tracy_zone.end(); + const id_len = switch (id) { .Integer => |id_val| blk: { if (id_val == 0) break :blk 1; @@ -194,6 +204,9 @@ fn astLocationToRange(loc: Ast.Location) types.Range { } fn publishDiagnostics(arena: *std.heap.ArenaAllocator, handle: DocumentStore.Handle, config: *const Config) !void { + const tracy_zone = tracy.trace(@src()); + defer tracy_zone.end(); + const tree = handle.tree; var diagnostics = std.ArrayList(types.Diagnostic).init(arena.allocator()); @@ -273,7 +286,16 @@ fn publishDiagnostics(arena: *std.heap.ArenaAllocator, handle: DocumentStore.Han }); } -fn typeToCompletion(arena: *std.heap.ArenaAllocator, list: *std.ArrayList(types.CompletionItem), field_access: analysis.FieldAccessReturn, orig_handle: *DocumentStore.Handle, config: *const Config) error{OutOfMemory}!void { +fn typeToCompletion( + arena: *std.heap.ArenaAllocator, + list: *std.ArrayList(types.CompletionItem), + field_access: analysis.FieldAccessReturn, + orig_handle: *DocumentStore.Handle, + config: *const Config, +) error{OutOfMemory}!void { + const tracy_zone = tracy.trace(@src()); + defer tracy_zone.end(); + const type_handle = field_access.original; switch (type_handle.type.data) { .slice => { @@ -327,7 +349,19 @@ fn typeToCompletion(arena: *std.heap.ArenaAllocator, list: *std.ArrayList(types. } } -fn nodeToCompletion(arena: *std.heap.ArenaAllocator, list: *std.ArrayList(types.CompletionItem), node_handle: analysis.NodeWithHandle, unwrapped: ?analysis.TypeWithHandle, orig_handle: *DocumentStore.Handle, is_type_val: bool, parent_is_type_val: ?bool, config: *const Config) error{OutOfMemory}!void { +fn nodeToCompletion( + arena: *std.heap.ArenaAllocator, + list: *std.ArrayList(types.CompletionItem), + node_handle: analysis.NodeWithHandle, + unwrapped: ?analysis.TypeWithHandle, + orig_handle: *DocumentStore.Handle, + is_type_val: bool, + parent_is_type_val: ?bool, + config: *const Config, +) error{OutOfMemory}!void { + const tracy_zone = tracy.trace(@src()); + defer tracy_zone.end(); + const node = node_handle.node; const handle = node_handle.handle; const tree = handle.tree; @@ -547,6 +581,9 @@ fn isSymbolChar(char: u8) bool { } fn gotoDefinitionSymbol(id: types.RequestId, arena: *std.heap.ArenaAllocator, decl_handle: analysis.DeclWithHandle, resolve_alias: bool) !void { + const tracy_zone = tracy.trace(@src()); + defer tracy_zone.end(); + var handle = decl_handle.handle; const location = switch (decl_handle.decl.*) { @@ -586,6 +623,9 @@ fn gotoDefinitionSymbol(id: types.RequestId, arena: *std.heap.ArenaAllocator, de } fn hoverSymbol(id: types.RequestId, arena: *std.heap.ArenaAllocator, decl_handle: analysis.DeclWithHandle) (std.os.WriteError || error{OutOfMemory})!void { + const tracy_zone = tracy.trace(@src()); + defer tracy_zone.end(); + const handle = decl_handle.handle; const tree = handle.tree; @@ -660,6 +700,9 @@ fn hoverSymbol(id: types.RequestId, arena: *std.heap.ArenaAllocator, decl_handle } fn getLabelGlobal(pos_index: usize, handle: *DocumentStore.Handle) !?analysis.DeclWithHandle { + const tracy_zone = tracy.trace(@src()); + defer tracy_zone.end(); + const name = identifierFromPosition(pos_index, handle.*); if (name.len == 0) return null; @@ -667,6 +710,9 @@ fn getLabelGlobal(pos_index: usize, handle: *DocumentStore.Handle) !?analysis.De } fn getSymbolGlobal(arena: *std.heap.ArenaAllocator, pos_index: usize, handle: *DocumentStore.Handle) !?analysis.DeclWithHandle { + const tracy_zone = tracy.trace(@src()); + defer tracy_zone.end(); + const name = identifierFromPosition(pos_index, handle.*); if (name.len == 0) return null; @@ -674,6 +720,9 @@ fn getSymbolGlobal(arena: *std.heap.ArenaAllocator, pos_index: usize, handle: *D } fn gotoDefinitionLabel(arena: *std.heap.ArenaAllocator, id: types.RequestId, pos_index: usize, handle: *DocumentStore.Handle, config: *const Config) !void { + const tracy_zone = tracy.trace(@src()); + defer tracy_zone.end(); + _ = config; const decl = (try getLabelGlobal(pos_index, handle)) orelse return try respondGeneric(id, null_result_response); @@ -681,6 +730,9 @@ fn gotoDefinitionLabel(arena: *std.heap.ArenaAllocator, id: types.RequestId, pos } fn gotoDefinitionGlobal(arena: *std.heap.ArenaAllocator, id: types.RequestId, pos_index: usize, handle: *DocumentStore.Handle, config: *const Config, resolve_alias: bool) !void { + const tracy_zone = tracy.trace(@src()); + defer tracy_zone.end(); + _ = config; const decl = (try getSymbolGlobal(arena, pos_index, handle)) orelse return try respondGeneric(id, null_result_response); @@ -688,6 +740,9 @@ fn gotoDefinitionGlobal(arena: *std.heap.ArenaAllocator, id: types.RequestId, po } fn hoverDefinitionLabel(arena: *std.heap.ArenaAllocator, id: types.RequestId, pos_index: usize, handle: *DocumentStore.Handle, config: *const Config) !void { + const tracy_zone = tracy.trace(@src()); + defer tracy_zone.end(); + _ = config; const decl = (try getLabelGlobal(pos_index, handle)) orelse return try respondGeneric(id, null_result_response); @@ -695,6 +750,9 @@ fn hoverDefinitionLabel(arena: *std.heap.ArenaAllocator, id: types.RequestId, po } fn hoverDefinitionBuiltin(arena: *std.heap.ArenaAllocator, id: types.RequestId, pos_index: usize, handle: *DocumentStore.Handle) !void { + const tracy_zone = tracy.trace(@src()); + defer tracy_zone.end(); + const name = identifierFromPosition(pos_index, handle.*); if (name.len == 0) return try respondGeneric(id, null_result_response); @@ -719,6 +777,9 @@ fn hoverDefinitionBuiltin(arena: *std.heap.ArenaAllocator, id: types.RequestId, } fn hoverDefinitionGlobal(arena: *std.heap.ArenaAllocator, id: types.RequestId, pos_index: usize, handle: *DocumentStore.Handle, config: *const Config) !void { + const tracy_zone = tracy.trace(@src()); + defer tracy_zone.end(); + _ = config; const decl = (try getSymbolGlobal(arena, pos_index, handle)) orelse return try respondGeneric(id, null_result_response); @@ -726,6 +787,9 @@ fn hoverDefinitionGlobal(arena: *std.heap.ArenaAllocator, id: types.RequestId, p } fn getSymbolFieldAccess(handle: *DocumentStore.Handle, arena: *std.heap.ArenaAllocator, position: offsets.DocumentPosition, range: analysis.SourceRange, config: *const Config) !?analysis.DeclWithHandle { + const tracy_zone = tracy.trace(@src()); + defer tracy_zone.end(); + _ = config; const name = identifierFromPosition(position.absolute_index, handle.*); @@ -755,16 +819,25 @@ fn getSymbolFieldAccess(handle: *DocumentStore.Handle, arena: *std.heap.ArenaAll } fn gotoDefinitionFieldAccess(arena: *std.heap.ArenaAllocator, id: types.RequestId, handle: *DocumentStore.Handle, position: offsets.DocumentPosition, range: analysis.SourceRange, config: *const Config, resolve_alias: bool) !void { + const tracy_zone = tracy.trace(@src()); + defer tracy_zone.end(); + const decl = (try getSymbolFieldAccess(handle, arena, position, range, config)) orelse return try respondGeneric(id, null_result_response); return try gotoDefinitionSymbol(id, arena, decl, resolve_alias); } fn hoverDefinitionFieldAccess(arena: *std.heap.ArenaAllocator, id: types.RequestId, handle: *DocumentStore.Handle, position: offsets.DocumentPosition, range: analysis.SourceRange, config: *const Config) !void { + const tracy_zone = tracy.trace(@src()); + defer tracy_zone.end(); + const decl = (try getSymbolFieldAccess(handle, arena, position, range, config)) orelse return try respondGeneric(id, null_result_response); return try hoverSymbol(id, arena, decl); } fn gotoDefinitionString(arena: *std.heap.ArenaAllocator, id: types.RequestId, pos_index: usize, handle: *DocumentStore.Handle, config: *const Config) !void { + const tracy_zone = tracy.trace(@src()); + defer tracy_zone.end(); + _ = config; const tree = handle.tree; @@ -791,6 +864,9 @@ fn gotoDefinitionString(arena: *std.heap.ArenaAllocator, id: types.RequestId, po } fn renameDefinitionGlobal(arena: *std.heap.ArenaAllocator, id: types.RequestId, handle: *DocumentStore.Handle, pos_index: usize, new_name: []const u8) !void { + const tracy_zone = tracy.trace(@src()); + defer tracy_zone.end(); + const decl = (try getSymbolGlobal(arena, pos_index, handle)) orelse return try respondGeneric(id, null_result_response); var workspace_edit = types.WorkspaceEdit{ @@ -804,6 +880,9 @@ fn renameDefinitionGlobal(arena: *std.heap.ArenaAllocator, id: types.RequestId, } fn renameDefinitionFieldAccess(arena: *std.heap.ArenaAllocator, id: types.RequestId, handle: *DocumentStore.Handle, position: offsets.DocumentPosition, range: analysis.SourceRange, new_name: []const u8, config: *const Config) !void { + const tracy_zone = tracy.trace(@src()); + defer tracy_zone.end(); + const decl = (try getSymbolFieldAccess(handle, arena, position, range, config)) orelse return try respondGeneric(id, null_result_response); var workspace_edit = types.WorkspaceEdit{ @@ -817,6 +896,9 @@ fn renameDefinitionFieldAccess(arena: *std.heap.ArenaAllocator, id: types.Reques } fn renameDefinitionLabel(arena: *std.heap.ArenaAllocator, id: types.RequestId, handle: *DocumentStore.Handle, pos_index: usize, new_name: []const u8) !void { + const tracy_zone = tracy.trace(@src()); + defer tracy_zone.end(); + const decl = (try getLabelGlobal(pos_index, handle)) orelse return try respondGeneric(id, null_result_response); var workspace_edit = types.WorkspaceEdit{ @@ -830,6 +912,9 @@ fn renameDefinitionLabel(arena: *std.heap.ArenaAllocator, id: types.RequestId, h } fn referencesDefinitionGlobal(arena: *std.heap.ArenaAllocator, id: types.RequestId, handle: *DocumentStore.Handle, pos_index: usize, include_decl: bool, skip_std_references: bool) !void { + const tracy_zone = tracy.trace(@src()); + defer tracy_zone.end(); + const decl = (try getSymbolGlobal(arena, pos_index, handle)) orelse return try respondGeneric(id, null_result_response); var locs = std.ArrayList(types.Location).init(arena.allocator()); try references.symbolReferences( @@ -849,6 +934,9 @@ fn referencesDefinitionGlobal(arena: *std.heap.ArenaAllocator, id: types.Request } fn referencesDefinitionFieldAccess(arena: *std.heap.ArenaAllocator, id: types.RequestId, handle: *DocumentStore.Handle, position: offsets.DocumentPosition, range: analysis.SourceRange, include_decl: bool, config: *const Config) !void { + const tracy_zone = tracy.trace(@src()); + defer tracy_zone.end(); + const decl = (try getSymbolFieldAccess(handle, arena, position, range, config)) orelse return try respondGeneric(id, null_result_response); var locs = std.ArrayList(types.Location).init(arena.allocator()); try references.symbolReferences(arena, &document_store, decl, offset_encoding, include_decl, &locs, std.ArrayList(types.Location).append, config.skip_std_references); @@ -859,6 +947,9 @@ fn referencesDefinitionFieldAccess(arena: *std.heap.ArenaAllocator, id: types.Re } fn referencesDefinitionLabel(arena: *std.heap.ArenaAllocator, id: types.RequestId, handle: *DocumentStore.Handle, pos_index: usize, include_decl: bool) !void { + const tracy_zone = tracy.trace(@src()); + defer tracy_zone.end(); + const decl = (try getLabelGlobal(pos_index, handle)) orelse return try respondGeneric(id, null_result_response); var locs = std.ArrayList(types.Location).init(arena.allocator()); try references.labelReferences(arena, decl, offset_encoding, include_decl, &locs, std.ArrayList(types.Location).append); @@ -869,6 +960,9 @@ fn referencesDefinitionLabel(arena: *std.heap.ArenaAllocator, id: types.RequestI } fn hasComment(tree: Ast.Tree, start_token: Ast.TokenIndex, end_token: Ast.TokenIndex) bool { + const tracy_zone = tracy.trace(@src()); + defer tracy_zone.end(); + const token_starts = tree.tokens.items(.start); const start = token_starts[start_token]; @@ -886,6 +980,9 @@ const DeclToCompletionContext = struct { }; fn declToCompletion(context: DeclToCompletionContext, decl_handle: analysis.DeclWithHandle) !void { + const tracy_zone = tracy.trace(@src()); + defer tracy_zone.end(); + const tree = decl_handle.handle.tree; switch (decl_handle.decl.*) { .ast_node => |node| try nodeToCompletion( @@ -967,6 +1064,9 @@ fn declToCompletion(context: DeclToCompletionContext, decl_handle: analysis.Decl } fn completeLabel(arena: *std.heap.ArenaAllocator, id: types.RequestId, pos_index: usize, handle: *DocumentStore.Handle, config: *const Config) !void { + const tracy_zone = tracy.trace(@src()); + defer tracy_zone.end(); + var completions = std.ArrayList(types.CompletionItem).init(arena.allocator()); const context = DeclToCompletionContext{ @@ -991,6 +1091,9 @@ fn completeLabel(arena: *std.heap.ArenaAllocator, id: types.RequestId, pos_index var builtin_completions: ?[]types.CompletionItem = null; fn completeBuiltin(arena: *std.heap.ArenaAllocator, id: types.RequestId, config: *const Config) !void { + const tracy_zone = tracy.trace(@src()); + defer tracy_zone.end(); + if (builtin_completions == null) { builtin_completions = try allocator.alloc(types.CompletionItem, data.builtins.len); for (data.builtins) |builtin, idx| { @@ -1033,6 +1136,9 @@ fn completeBuiltin(arena: *std.heap.ArenaAllocator, id: types.RequestId, config: } fn completeGlobal(arena: *std.heap.ArenaAllocator, id: types.RequestId, pos_index: usize, handle: *DocumentStore.Handle, config: *const Config) !void { + const tracy_zone = tracy.trace(@src()); + defer tracy_zone.end(); + var completions = std.ArrayList(types.CompletionItem).init(arena.allocator()); const context = DeclToCompletionContext{ @@ -1056,6 +1162,9 @@ fn completeGlobal(arena: *std.heap.ArenaAllocator, id: types.RequestId, pos_inde } fn completeFieldAccess(arena: *std.heap.ArenaAllocator, id: types.RequestId, handle: *DocumentStore.Handle, position: offsets.DocumentPosition, range: analysis.SourceRange, config: *const Config) !void { + const tracy_zone = tracy.trace(@src()); + defer tracy_zone.end(); + var completions = std.ArrayList(types.CompletionItem).init(arena.allocator()); const line_mem_start = @ptrToInt(position.line.ptr) - @ptrToInt(handle.document.mem.ptr); @@ -1081,6 +1190,9 @@ fn completeFieldAccess(arena: *std.heap.ArenaAllocator, id: types.RequestId, han } fn completeError(arena: *std.heap.ArenaAllocator, id: types.RequestId, handle: *DocumentStore.Handle, config: *const Config) !void { + const tracy_zone = tracy.trace(@src()); + defer tracy_zone.end(); + const completions = try document_store.errorCompletionItems(arena, handle); truncateCompletions(completions, config.max_detail_length); logger.debug("Completing error:", .{}); @@ -1097,6 +1209,9 @@ fn completeError(arena: *std.heap.ArenaAllocator, id: types.RequestId, handle: * } fn completeDot(arena: *std.heap.ArenaAllocator, id: types.RequestId, handle: *DocumentStore.Handle, config: *const Config) !void { + const tracy_zone = tracy.trace(@src()); + defer tracy_zone.end(); + var completions = try document_store.enumCompletionItems(arena, handle); truncateCompletions(completions, config.max_detail_length); @@ -1112,6 +1227,9 @@ fn completeDot(arena: *std.heap.ArenaAllocator, id: types.RequestId, handle: *Do } fn documentSymbol(arena: *std.heap.ArenaAllocator, id: types.RequestId, handle: *DocumentStore.Handle) !void { + const tracy_zone = tracy.trace(@src()); + defer tracy_zone.end(); + try send(arena, types.Response{ .id = id, .result = .{ .DocumentSymbols = try analysis.getDocumentSymbols(arena.allocator(), handle.tree, offset_encoding) }, @@ -1119,6 +1237,9 @@ fn documentSymbol(arena: *std.heap.ArenaAllocator, id: types.RequestId, handle: } fn loadConfigFile(file_path: []const u8) ?Config { + const tracy_zone = tracy.trace(@src()); + defer tracy_zone.end(); + var file = std.fs.cwd().openFile(file_path, .{}) catch |err| { if (err != error.FileNotFound) logger.warn("Error while reading configuration file: {}", .{err}); @@ -1148,12 +1269,18 @@ fn loadConfigFile(file_path: []const u8) ?Config { } fn loadConfigInFolder(folder_path: []const u8) ?Config { + const tracy_zone = tracy.trace(@src()); + defer tracy_zone.end(); + const full_path = std.fs.path.resolve(allocator, &.{ folder_path, "zls.json" }) catch return null; defer allocator.free(full_path); return loadConfigFile(full_path); } fn initializeHandler(arena: *std.heap.ArenaAllocator, id: types.RequestId, req: requests.Initialize, config: *const Config) !void { + const tracy_zone = tracy.trace(@src()); + defer tracy_zone.end(); + _ = config; for (req.params.capabilities.offsetEncoding.value) |encoding| { @@ -1287,6 +1414,9 @@ fn shutdownHandler(arena: *std.heap.ArenaAllocator, id: types.RequestId, config: } fn openDocumentHandler(arena: *std.heap.ArenaAllocator, id: types.RequestId, req: requests.OpenDocument, config: *const Config) !void { + const tracy_zone = tracy.trace(@src()); + defer tracy_zone.end(); + const handle = try document_store.openDocument(req.params.textDocument.uri, req.params.textDocument.text); try publishDiagnostics(arena, handle.*, config); @@ -1295,6 +1425,9 @@ fn openDocumentHandler(arena: *std.heap.ArenaAllocator, id: types.RequestId, req } fn changeDocumentHandler(arena: *std.heap.ArenaAllocator, id: types.RequestId, req: requests.ChangeDocument, config: *const Config) !void { + const tracy_zone = tracy.trace(@src()); + defer tracy_zone.end(); + _ = id; const handle = document_store.getHandle(req.params.textDocument.uri) orelse { @@ -1307,6 +1440,9 @@ fn changeDocumentHandler(arena: *std.heap.ArenaAllocator, id: types.RequestId, r } fn saveDocumentHandler(arena: *std.heap.ArenaAllocator, id: types.RequestId, req: requests.SaveDocument, config: *const Config) error{OutOfMemory}!void { + const tracy_zone = tracy.trace(@src()); + defer tracy_zone.end(); + _ = config; _ = id; _ = arena; @@ -1318,6 +1454,9 @@ fn saveDocumentHandler(arena: *std.heap.ArenaAllocator, id: types.RequestId, req } fn closeDocumentHandler(arena: *std.heap.ArenaAllocator, id: types.RequestId, req: requests.CloseDocument, config: *const Config) error{}!void { + const tracy_zone = tracy.trace(@src()); + defer tracy_zone.end(); + _ = config; _ = id; _ = arena; @@ -1325,6 +1464,9 @@ fn closeDocumentHandler(arena: *std.heap.ArenaAllocator, id: types.RequestId, re } fn semanticTokensFullHandler(arena: *std.heap.ArenaAllocator, id: types.RequestId, req: requests.SemanticTokensFull, config: *const Config) !void { + const tracy_zone = tracy.trace(@src()); + defer tracy_zone.end(); + if (config.enable_semantic_tokens) blk: { const handle = document_store.getHandle(req.params.textDocument.uri) orelse { logger.warn("Trying to get semantic tokens of non existent document {s}", .{req.params.textDocument.uri}); @@ -1343,6 +1485,9 @@ fn semanticTokensFullHandler(arena: *std.heap.ArenaAllocator, id: types.RequestI } fn completionHandler(arena: *std.heap.ArenaAllocator, id: types.RequestId, req: requests.Completion, config: *const Config) !void { + const tracy_zone = tracy.trace(@src()); + defer tracy_zone.end(); + const handle = document_store.getHandle(req.params.textDocument.uri) orelse { logger.warn("Trying to complete in non existent document {s}", .{req.params.textDocument.uri}); return try respondGeneric(id, no_completions_response); @@ -1366,6 +1511,9 @@ fn completionHandler(arena: *std.heap.ArenaAllocator, id: types.RequestId, req: } fn signatureHelpHandler(arena: *std.heap.ArenaAllocator, id: types.RequestId, req: requests.SignatureHelp, config: *const Config) !void { + const tracy_zone = tracy.trace(@src()); + defer tracy_zone.end(); + _ = config; const getSignatureInfo = @import("signature_help.zig").getSignatureInfo; @@ -1400,6 +1548,9 @@ fn signatureHelpHandler(arena: *std.heap.ArenaAllocator, id: types.RequestId, re } fn gotoHandler(arena: *std.heap.ArenaAllocator, id: types.RequestId, req: requests.GotoDefinition, config: *const Config, resolve_alias: bool) !void { + const tracy_zone = tracy.trace(@src()); + defer tracy_zone.end(); + const handle = document_store.getHandle(req.params.textDocument.uri) orelse { logger.warn("Trying to go to definition in non existent document {s}", .{req.params.textDocument.uri}); return try respondGeneric(id, null_result_response); @@ -1422,14 +1573,23 @@ fn gotoHandler(arena: *std.heap.ArenaAllocator, id: types.RequestId, req: reques } fn gotoDefinitionHandler(arena: *std.heap.ArenaAllocator, id: types.RequestId, req: requests.GotoDefinition, config: *const Config) !void { + const tracy_zone = tracy.trace(@src()); + defer tracy_zone.end(); + try gotoHandler(arena, id, req, config, true); } fn gotoDeclarationHandler(arena: *std.heap.ArenaAllocator, id: types.RequestId, req: requests.GotoDeclaration, config: *const Config) !void { + const tracy_zone = tracy.trace(@src()); + defer tracy_zone.end(); + try gotoHandler(arena, id, req, config, false); } fn hoverHandler(arena: *std.heap.ArenaAllocator, id: types.RequestId, req: requests.Hover, config: *const Config) !void { + const tracy_zone = tracy.trace(@src()); + defer tracy_zone.end(); + const handle = document_store.getHandle(req.params.textDocument.uri) orelse { logger.warn("Trying to get hover in non existent document {s}", .{req.params.textDocument.uri}); return try respondGeneric(id, null_result_response); @@ -1451,6 +1611,9 @@ fn hoverHandler(arena: *std.heap.ArenaAllocator, id: types.RequestId, req: reque } fn documentSymbolsHandler(arena: *std.heap.ArenaAllocator, id: types.RequestId, req: requests.DocumentSymbols, config: *const Config) !void { + const tracy_zone = tracy.trace(@src()); + defer tracy_zone.end(); + _ = config; const handle = document_store.getHandle(req.params.textDocument.uri) orelse { @@ -1461,6 +1624,9 @@ fn documentSymbolsHandler(arena: *std.heap.ArenaAllocator, id: types.RequestId, } fn formattingHandler(arena: *std.heap.ArenaAllocator, id: types.RequestId, req: requests.Formatting, config: *const Config) !void { + const tracy_zone = tracy.trace(@src()); + defer tracy_zone.end(); + if (config.zig_exe_path) |zig_exe_path| { const handle = document_store.getHandle(req.params.textDocument.uri) orelse { logger.warn("Trying to got to definition in non existent document {s}", .{req.params.textDocument.uri}); @@ -1503,6 +1669,9 @@ fn formattingHandler(arena: *std.heap.ArenaAllocator, id: types.RequestId, req: } fn renameHandler(arena: *std.heap.ArenaAllocator, id: types.RequestId, req: requests.Rename, config: *const Config) !void { + const tracy_zone = tracy.trace(@src()); + defer tracy_zone.end(); + const handle = document_store.getHandle(req.params.textDocument.uri) orelse { logger.warn("Trying to rename in non existent document {s}", .{req.params.textDocument.uri}); return try respondGeneric(id, null_result_response); @@ -1524,6 +1693,9 @@ fn renameHandler(arena: *std.heap.ArenaAllocator, id: types.RequestId, req: requ } fn didChangeConfigurationHandler(arena: *std.heap.ArenaAllocator, id: types.RequestId, req: requests.Configuration, config: *Config) !void { + const tracy_zone = tracy.trace(@src()); + defer tracy_zone.end(); + _ = arena; _ = id; inline for (std.meta.fields(Config)) |field| { @@ -1535,6 +1707,9 @@ fn didChangeConfigurationHandler(arena: *std.heap.ArenaAllocator, id: types.Requ } fn referencesHandler(arena: *std.heap.ArenaAllocator, id: types.RequestId, req: requests.References, config: *const Config) !void { + const tracy_zone = tracy.trace(@src()); + defer tracy_zone.end(); + const handle = document_store.getHandle(req.params.textDocument.uri) orelse { logger.warn("Trying to get references in non existent document {s}", .{req.params.textDocument.uri}); return try respondGeneric(id, null_result_response); @@ -1563,6 +1738,9 @@ fn extractErr(val: anytype) anyerror { } fn processJsonRpc(arena: *std.heap.ArenaAllocator, parser: *std.json.Parser, json: []const u8, config: *Config) !void { + const tracy_zone = tracy.trace(@src()); + defer tracy_zone.end(); + var tree = try parser.parse(json); defer tree.deinit(); @@ -1673,14 +1851,18 @@ const stack_frames = switch (zig_builtin.mode) { .Debug => 10, else => 0, }; -var gpa_state = std.heap.GeneralPurposeAllocator(.{ .stack_trace_frames = stack_frames }){}; +var gpa_state = std.heap.GeneralPurposeAllocator(.{ .stack_trace_frames = stack_frames }){ .backing_allocator = std.heap.page_allocator }; pub fn main() anyerror!void { defer _ = gpa_state.deinit(); defer keep_running = false; - // allocator = &gpa_state.allocator; + allocator = gpa_state.allocator(); + if (tracy.enable_allocation) { + allocator = tracy.tracyAllocator(allocator).allocator(); + } + defer _ = gpa_state.deinit(); // @TODO Using the GPA here, realloc calls hang currently for some reason - allocator = std.heap.page_allocator; + // allocator = std.heap.page_allocator; analysis.init(allocator); defer analysis.deinit(); diff --git a/src/tracy.zig b/src/tracy.zig new file mode 100644 index 0000000..07553bc --- /dev/null +++ b/src/tracy.zig @@ -0,0 +1,333 @@ +//! Tracy bindings from Zig compiler +// +// The MIT License (Expat) +// +// Copyright (c) 2015-2022, Zig contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +const std = @import("std"); +const builtin = @import("builtin"); +const build_options = @import("build_options"); + +pub const enable = if (builtin.is_test) false else build_options.enable_tracy; +pub const enable_allocation = enable and build_options.enable_tracy_allocation; +pub const enable_callstack = enable and build_options.enable_tracy_callstack; + +// TODO: make this configurable +const callstack_depth = 10; + +const ___tracy_c_zone_context = extern struct { + id: u32, + active: c_int, + + pub inline fn end(self: @This()) void { + ___tracy_emit_zone_end(self); + } + + pub inline fn addText(self: @This(), text: []const u8) void { + ___tracy_emit_zone_text(self, text.ptr, text.len); + } + + pub inline fn setName(self: @This(), name: []const u8) void { + ___tracy_emit_zone_name(self, name.ptr, name.len); + } + + pub inline fn setColor(self: @This(), color: u32) void { + ___tracy_emit_zone_color(self, color); + } + + pub inline fn setValue(self: @This(), value: u64) void { + ___tracy_emit_zone_value(self, value); + } +}; + +pub const Ctx = if (enable) ___tracy_c_zone_context else struct { + pub inline fn end(self: @This()) void { + _ = self; + } + + pub inline fn addText(self: @This(), text: []const u8) void { + _ = self; + _ = text; + } + + pub inline fn setName(self: @This(), name: []const u8) void { + _ = self; + _ = name; + } + + pub inline fn setColor(self: @This(), color: u32) void { + _ = self; + _ = color; + } + + pub inline fn setValue(self: @This(), value: u64) void { + _ = self; + _ = value; + } +}; + +pub inline fn trace(comptime src: std.builtin.SourceLocation) Ctx { + if (!enable) return .{}; + + if (enable_callstack) { + return ___tracy_emit_zone_begin_callstack(&.{ + .name = null, + .function = src.fn_name.ptr, + .file = src.file.ptr, + .line = src.line, + .color = 0, + }, callstack_depth, 1); + } else { + return ___tracy_emit_zone_begin(&.{ + .name = null, + .function = src.fn_name.ptr, + .file = src.file.ptr, + .line = src.line, + .color = 0, + }, 1); + } +} + +pub inline fn traceNamed(comptime src: std.builtin.SourceLocation, comptime name: [:0]const u8) Ctx { + if (!enable) return .{}; + + if (enable_callstack) { + return ___tracy_emit_zone_begin_callstack(&.{ + .name = name.ptr, + .function = src.fn_name.ptr, + .file = src.file.ptr, + .line = src.line, + .color = 0, + }, callstack_depth, 1); + } else { + return ___tracy_emit_zone_begin(&.{ + .name = name.ptr, + .function = src.fn_name.ptr, + .file = src.file.ptr, + .line = src.line, + .color = 0, + }, 1); + } +} + +pub fn tracyAllocator(allocator: std.mem.Allocator) TracyAllocator(null) { + return TracyAllocator(null).init(allocator); +} + +pub fn TracyAllocator(comptime name: ?[:0]const u8) type { + return struct { + parent_allocator: std.mem.Allocator, + + const Self = @This(); + + pub fn init(parent_allocator: std.mem.Allocator) Self { + return .{ + .parent_allocator = parent_allocator, + }; + } + + pub fn allocator(self: *Self) std.mem.Allocator { + return std.mem.Allocator.init(self, allocFn, resizeFn, freeFn); + } + + fn allocFn(self: *Self, len: usize, ptr_align: u29, len_align: u29, ret_addr: usize) std.mem.Allocator.Error![]u8 { + const result = self.parent_allocator.rawAlloc(len, ptr_align, len_align, ret_addr); + if (result) |data| { + if (data.len != 0) { + if (name) |n| { + allocNamed(data.ptr, data.len, n); + } else { + alloc(data.ptr, data.len); + } + } + } else |_| { + messageColor("allocation failed", 0xFF0000); + } + return result; + } + + fn resizeFn(self: *Self, buf: []u8, buf_align: u29, new_len: usize, len_align: u29, ret_addr: usize) ?usize { + if (self.parent_allocator.rawResize(buf, buf_align, new_len, len_align, ret_addr)) |resized_len| { + if (name) |n| { + freeNamed(buf.ptr, n); + allocNamed(buf.ptr, resized_len, n); + } else { + free(buf.ptr); + alloc(buf.ptr, resized_len); + } + + return resized_len; + } + + // during normal operation the compiler hits this case thousands of times due to this + // emitting messages for it is both slow and causes clutter + return null; + } + + fn freeFn(self: *Self, buf: []u8, buf_align: u29, ret_addr: usize) void { + self.parent_allocator.rawFree(buf, buf_align, ret_addr); + // this condition is to handle free being called on an empty slice that was never even allocated + // example case: `std.process.getSelfExeSharedLibPaths` can return `&[_][:0]u8{}` + if (buf.len != 0) { + if (name) |n| { + freeNamed(buf.ptr, n); + } else { + free(buf.ptr); + } + } + } + }; +} + +// This function only accepts comptime known strings, see `messageCopy` for runtime strings +pub inline fn message(comptime msg: [:0]const u8) void { + if (!enable) return; + ___tracy_emit_messageL(msg.ptr, if (enable_callstack) callstack_depth else 0); +} + +// This function only accepts comptime known strings, see `messageColorCopy` for runtime strings +pub inline fn messageColor(comptime msg: [:0]const u8, color: u32) void { + if (!enable) return; + ___tracy_emit_messageLC(msg.ptr, color, if (enable_callstack) callstack_depth else 0); +} + +pub inline fn messageCopy(msg: []const u8) void { + if (!enable) return; + ___tracy_emit_message(msg.ptr, msg.len, if (enable_callstack) callstack_depth else 0); +} + +pub inline fn messageColorCopy(msg: [:0]const u8, color: u32) void { + if (!enable) return; + ___tracy_emit_messageC(msg.ptr, msg.len, color, if (enable_callstack) callstack_depth else 0); +} + +pub inline fn frameMark() void { + if (!enable) return; + ___tracy_emit_frame_mark(null); +} + +pub inline fn frameMarkNamed(comptime name: [:0]const u8) void { + if (!enable) return; + ___tracy_emit_frame_mark(name.ptr); +} + +pub inline fn namedFrame(comptime name: [:0]const u8) Frame(name) { + frameMarkStart(name); + return .{}; +} + +pub fn Frame(comptime name: [:0]const u8) type { + return struct { + pub fn end(_: @This()) void { + frameMarkEnd(name); + } + }; +} + +inline fn frameMarkStart(comptime name: [:0]const u8) void { + if (!enable) return; + ___tracy_emit_frame_mark_start(name.ptr); +} + +inline fn frameMarkEnd(comptime name: [:0]const u8) void { + if (!enable) return; + ___tracy_emit_frame_mark_end(name.ptr); +} + +extern fn ___tracy_emit_frame_mark_start(name: [*:0]const u8) void; +extern fn ___tracy_emit_frame_mark_end(name: [*:0]const u8) void; + +inline fn alloc(ptr: [*]u8, len: usize) void { + if (!enable) return; + + if (enable_callstack) { + ___tracy_emit_memory_alloc_callstack(ptr, len, callstack_depth, 0); + } else { + ___tracy_emit_memory_alloc(ptr, len, 0); + } +} + +inline fn allocNamed(ptr: [*]u8, len: usize, comptime name: [:0]const u8) void { + if (!enable) return; + + if (enable_callstack) { + ___tracy_emit_memory_alloc_callstack_named(ptr, len, callstack_depth, 0, name.ptr); + } else { + ___tracy_emit_memory_alloc_named(ptr, len, 0, name.ptr); + } +} + +inline fn free(ptr: [*]u8) void { + if (!enable) return; + + if (enable_callstack) { + ___tracy_emit_memory_free_callstack(ptr, callstack_depth, 0); + } else { + ___tracy_emit_memory_free(ptr, 0); + } +} + +inline fn freeNamed(ptr: [*]u8, comptime name: [:0]const u8) void { + if (!enable) return; + + if (enable_callstack) { + ___tracy_emit_memory_free_callstack_named(ptr, callstack_depth, 0, name.ptr); + } else { + ___tracy_emit_memory_free_named(ptr, 0, name.ptr); + } +} + +extern fn ___tracy_emit_zone_begin( + srcloc: *const ___tracy_source_location_data, + active: c_int, +) ___tracy_c_zone_context; +extern fn ___tracy_emit_zone_begin_callstack( + srcloc: *const ___tracy_source_location_data, + depth: c_int, + active: c_int, +) ___tracy_c_zone_context; +extern fn ___tracy_emit_zone_text(ctx: ___tracy_c_zone_context, txt: [*]const u8, size: usize) void; +extern fn ___tracy_emit_zone_name(ctx: ___tracy_c_zone_context, txt: [*]const u8, size: usize) void; +extern fn ___tracy_emit_zone_color(ctx: ___tracy_c_zone_context, color: u32) void; +extern fn ___tracy_emit_zone_value(ctx: ___tracy_c_zone_context, value: u64) void; +extern fn ___tracy_emit_zone_end(ctx: ___tracy_c_zone_context) void; +extern fn ___tracy_emit_memory_alloc(ptr: *const anyopaque, size: usize, secure: c_int) void; +extern fn ___tracy_emit_memory_alloc_callstack(ptr: *const anyopaque, size: usize, depth: c_int, secure: c_int) void; +extern fn ___tracy_emit_memory_free(ptr: *const anyopaque, secure: c_int) void; +extern fn ___tracy_emit_memory_free_callstack(ptr: *const anyopaque, depth: c_int, secure: c_int) void; +extern fn ___tracy_emit_memory_alloc_named(ptr: *const anyopaque, size: usize, secure: c_int, name: [*:0]const u8) void; +extern fn ___tracy_emit_memory_alloc_callstack_named(ptr: *const anyopaque, size: usize, depth: c_int, secure: c_int, name: [*:0]const u8) void; +extern fn ___tracy_emit_memory_free_named(ptr: *const anyopaque, secure: c_int, name: [*:0]const u8) void; +extern fn ___tracy_emit_memory_free_callstack_named(ptr: *const anyopaque, depth: c_int, secure: c_int, name: [*:0]const u8) void; +extern fn ___tracy_emit_message(txt: [*]const u8, size: usize, callstack: c_int) void; +extern fn ___tracy_emit_messageL(txt: [*:0]const u8, callstack: c_int) void; +extern fn ___tracy_emit_messageC(txt: [*]const u8, size: usize, color: u32, callstack: c_int) void; +extern fn ___tracy_emit_messageLC(txt: [*:0]const u8, color: u32, callstack: c_int) void; +extern fn ___tracy_emit_frame_mark(name: ?[*:0]const u8) void; + +const ___tracy_source_location_data = extern struct { + name: ?[*:0]const u8, + function: [*:0]const u8, + file: [*:0]const u8, + line: u32, + color: u32, +};