| 179 | } |
| 180 | |
| 181 | static bool gjs_dump_memory_info(JSContext* cx, unsigned argc, JS::Value* vp) { |
| 182 | JS::CallArgs args = JS::CallArgsFromVp(argc, vp); |
| 183 | |
| 184 | Gjs::AutoChar filename; |
| 185 | if (!gjs_parse_call_args(cx, "dumpMemoryInfo", args, "|F", "filename", |
| 186 | &filename)) |
| 187 | return false; |
| 188 | |
| 189 | int64_t gc_counters[Gjs::GCCounters::N_COUNTERS]; |
| 190 | |
| 191 | // The object returned from NewMemoryInfoObject has gcBytes and mallocBytes |
| 192 | // properties which are the sum (over all zones) of bytes used. gcBytes is |
| 193 | // the number of bytes in garbage-collectable things (GC things). |
| 194 | // mallocBytes is the number of bytes allocated with malloc (reported with |
| 195 | // JS::AddAssociatedMemory). |
| 196 | // |
| 197 | // This info leaks internal state of the JS engine, which is why it is not |
| 198 | // returned to the caller, only dumped to a file and piped to Sysprof. |
| 199 | // |
| 200 | // The object also has a zone property with its own gcBytes and mallocBytes |
| 201 | // properties, representing the bytes used in the zone that the memory |
| 202 | // object belongs to. We only have one zone in GJS's context, so |
| 203 | // zone.gcBytes and zone.mallocBytes are a good measure for how much memory |
| 204 | // the actual user program is occupying. These are the values that we expose |
| 205 | // as counters in Sysprof. The difference between these values and the sum |
| 206 | // values is due to the self-hosting zone and atoms zone, that represent |
| 207 | // overhead of the JS engine. |
| 208 | |
| 209 | JS::RootedObject gc_info(cx, js::gc::NewMemoryInfoObject(cx)); |
| 210 | if (!gc_info) |
| 211 | return false; |
| 212 | |
| 213 | const GjsAtoms& atoms = GjsContextPrivate::atoms(cx); |
| 214 | int32_t val; |
| 215 | JS::RootedObject zone_info(cx); |
| 216 | if (!gjs_object_require_property(cx, gc_info, "gc.zone", atoms.zone(), |
| 217 | &zone_info) || |
| 218 | !gjs_object_require_property(cx, zone_info, "gc.zone.gcBytes", |
| 219 | atoms.gc_bytes(), &val)) |
| 220 | return false; |
| 221 | gc_counters[Gjs::GCCounters::GC_HEAP_BYTES] = static_cast<int64_t>(val); |
| 222 | if (!gjs_object_require_property(cx, zone_info, "gc.zone.mallocBytes", |
| 223 | atoms.malloc_bytes(), &val)) |
| 224 | return false; |
| 225 | gc_counters[Gjs::GCCounters::MALLOC_HEAP_BYTES] = static_cast<int64_t>(val); |
| 226 | |
| 227 | auto* gjs = GjsContextPrivate::from_cx(cx); |
| 228 | if (gjs->profiler() && |
| 229 | !gjs_profiler_sample_gc_memory_info(gjs->profiler(), gc_counters)) { |
| 230 | gjs_throw(cx, "Could not write GC counters to profiler"); |
| 231 | return false; |
| 232 | } |
| 233 | |
| 234 | LogFile file(filename); |
| 235 | if (file.has_error()) { |
| 236 | gjs_throw(cx, "Cannot dump memory info to %s: %s", filename.get(), |
| 237 | file.errmsg()); |
| 238 | return false; |
nothing calls this directly
no test coverage detected