1 module served.workers.profilegc; 2 3 import std.algorithm; 4 import std.array; 5 import std.ascii : isDigit; 6 import std.conv; 7 import std.experimental.logger; 8 import std.format; 9 import std.string; 10 11 import served.types; 12 13 import workspaced.api; 14 15 struct ProfileGCEntry 16 { 17 size_t bytesAllocated; 18 size_t allocationCount; 19 string type; 20 string uri, displayFile; 21 uint line; 22 } 23 24 struct ProfileGCCache 25 { 26 struct PerDocumentCache 27 { 28 ProfileGCEntry[] entries; 29 30 auto process(DocumentUri relativeToUri, scope const(char)[] content) 31 { 32 entries = null; 33 foreach (line; content.lineSplitter) 34 { 35 auto cols = line.split; 36 if (cols.length < 5) 37 continue; 38 auto typeStart = cols[2].ptr - line.ptr; 39 if (typeStart < 0 || typeStart > line.length) 40 typeStart = 0; 41 auto fileStart = line.lastIndexOfAny(" \t"); 42 if (fileStart != -1) 43 { 44 fileStart++; 45 auto colon = line.indexOf(":", fileStart); 46 if (colon != -1 && line[colon + 1 .. $].strip.all!isDigit) 47 { 48 auto file = line[fileStart .. colon]; 49 auto lineNo = line[colon + 1 .. $].strip.to!uint; 50 entries.assumeSafeAppend ~= ProfileGCEntry( 51 cols[0].to!size_t, 52 cols[1].to!size_t, 53 line[typeStart .. fileStart - 1].strip.idup, 54 uriBuildNormalized(relativeToUri, file), 55 file.idup, 56 lineNo 57 ); 58 } 59 } 60 } 61 return entries; 62 } 63 } 64 65 PerDocumentCache[DocumentUri] caches; 66 67 void update(DocumentUri uri) 68 { 69 import std.file : FileException; 70 71 try 72 { 73 auto profileGC = documents.getOrFromFilesystem(uri); 74 trace("Processing profilegc.log ", uri); 75 auto entries = caches.require(uri).process(uri.uriDirName, profileGC.rawText); 76 // trace("Processed: ", entries); 77 } 78 catch (FileException e) 79 { 80 trace("File Exception processing profilegc: ", e.msg); 81 caches.remove(uri); 82 } 83 catch (Exception e) 84 { 85 trace("Exception processing profilegc: ", e); 86 caches.remove(uri); 87 } 88 } 89 90 void clear(DocumentUri uri) 91 { 92 trace("Clearing profilegc.log cache from ", uri); 93 caches.remove(uri); 94 } 95 } 96 97 package __gshared ProfileGCCache profileGCCache; 98 99 @protocolMethod("textDocument/codeLens") 100 CodeLens[] provideProfileGCCodeLens(CodeLensParams params) 101 { 102 if (!config(params.textDocument.uri).d.enableGCProfilerDecorations) 103 return null; 104 105 auto lenses = appender!(CodeLens[]); 106 foreach (url, cache; profileGCCache.caches) 107 { 108 foreach (entry; cache.entries) 109 { 110 if (entry.uri == params.textDocument.uri) 111 { 112 lenses ~= CodeLens( 113 TextRange(entry.line - 1, 0, entry.line - 1, 1), 114 Command(format!"%s bytes allocated / %s allocations"(entry.bytesAllocated, entry.allocationCount)).opt 115 ); 116 } 117 } 118 } 119 return lenses.data; 120 } 121 122 @protocolMethod("served/getProfileGCEntries") 123 ProfileGCEntry[] getProfileGCEntries() 124 { 125 auto lenses = appender!(ProfileGCEntry[]); 126 foreach (url, cache; profileGCCache.caches) 127 lenses ~= cache.entries; 128 return lenses.data; 129 } 130 131 @onRegisteredComponents 132 void setupProfileGCWatchers() 133 { 134 if (capabilities 135 .workspace.orDefault 136 .didChangeWatchedFiles.orDefault 137 .dynamicRegistration.orDefault) 138 { 139 rpc.registerCapability( 140 "profilegc.watchfiles", 141 "workspace/didChangeWatchedFiles", 142 DidChangeWatchedFilesRegistrationOptions( 143 [ 144 FileSystemWatcher("**/profilegc.log") 145 ] 146 ) 147 ); 148 } 149 } 150 151 @onProjectAvailable 152 void onProfileGCProjectAvailable(WorkspaceD.Instance instance, string dir, string uri) 153 { 154 profileGCCache.update(uri.chomp("/") ~ "/profilegc.log"); 155 } 156 157 @protocolNotification("workspace/didChangeWatchedFiles") 158 void onChangeProfileGC(DidChangeWatchedFilesParams params) 159 { 160 foreach (change; params.changes) 161 { 162 if (!change.uri.endsWith("profilegc.log")) 163 continue; 164 165 if (change.type == FileChangeType.created 166 || change.type == FileChangeType.changed) 167 profileGCCache.update(change.uri); 168 else if (change.type == FileChangeType.deleted) 169 profileGCCache.clear(change.uri); 170 } 171 }