MemoryMetrics.h 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976
  1. /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  2. * This Source Code Form is subject to the terms of the Mozilla Public
  3. * License, v. 2.0. If a copy of the MPL was not distributed with this
  4. * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  5. #ifndef js_MemoryMetrics_h
  6. #define js_MemoryMetrics_h
  7. // These declarations are highly likely to change in the future. Depend on them
  8. // at your own risk.
  9. #include "mozilla/MemoryReporting.h"
  10. #include "mozilla/TypeTraits.h"
  11. #include <string.h>
  12. #include "jsalloc.h"
  13. #include "jspubtd.h"
  14. #include "js/HashTable.h"
  15. #include "js/TracingAPI.h"
  16. #include "js/Utility.h"
  17. #include "js/Vector.h"
  18. class nsISupports; // Needed for ObjectPrivateVisitor.
  19. namespace JS {
  20. struct TabSizes
  21. {
  22. enum Kind {
  23. Objects,
  24. Strings,
  25. Private,
  26. Other
  27. };
  28. TabSizes()
  29. : objects(0)
  30. , strings(0)
  31. , private_(0)
  32. , other(0)
  33. {
  34. }
  35. void add(Kind kind, size_t n) {
  36. switch (kind) {
  37. case Objects: objects += n; break;
  38. case Strings: strings += n; break;
  39. case Private: private_ += n; break;
  40. case Other: other += n; break;
  41. default: MOZ_CRASH("bad TabSizes kind");
  42. }
  43. }
  44. size_t objects;
  45. size_t strings;
  46. size_t private_;
  47. size_t other;
  48. };
  49. /** These are the measurements used by Servo. */
  50. struct ServoSizes
  51. {
  52. enum Kind {
  53. GCHeapUsed,
  54. GCHeapUnused,
  55. GCHeapAdmin,
  56. GCHeapDecommitted,
  57. MallocHeap,
  58. NonHeap,
  59. Ignore
  60. };
  61. ServoSizes() = default;
  62. void add(Kind kind, size_t n) {
  63. switch (kind) {
  64. case GCHeapUsed: gcHeapUsed += n; break;
  65. case GCHeapUnused: gcHeapUnused += n; break;
  66. case GCHeapAdmin: gcHeapAdmin += n; break;
  67. case GCHeapDecommitted: gcHeapDecommitted += n; break;
  68. case MallocHeap: mallocHeap += n; break;
  69. case NonHeap: nonHeap += n; break;
  70. case Ignore: /* do nothing */ break;
  71. default: MOZ_CRASH("bad ServoSizes kind");
  72. }
  73. }
  74. size_t gcHeapUsed = 0;
  75. size_t gcHeapUnused = 0;
  76. size_t gcHeapAdmin = 0;
  77. size_t gcHeapDecommitted = 0;
  78. size_t mallocHeap = 0;
  79. size_t nonHeap = 0;
  80. };
  81. } // namespace JS
  82. namespace js {
  83. /**
  84. * In memory reporting, we have concept of "sundries", line items which are too
  85. * small to be worth reporting individually. Under some circumstances, a memory
  86. * reporter gets tossed into the sundries bucket if it's smaller than
  87. * MemoryReportingSundriesThreshold() bytes.
  88. *
  89. * We need to define this value here, rather than in the code which actually
  90. * generates the memory reports, because NotableStringInfo uses this value.
  91. */
  92. JS_FRIEND_API(size_t) MemoryReportingSundriesThreshold();
  93. /**
  94. * This hash policy avoids flattening ropes (which perturbs the site being
  95. * measured and requires a JSContext) at the expense of doing a FULL ROPE COPY
  96. * on every hash and match! Beware.
  97. */
  98. struct InefficientNonFlatteningStringHashPolicy
  99. {
  100. typedef JSString* Lookup;
  101. static HashNumber hash(const Lookup& l);
  102. static bool match(const JSString* const& k, const Lookup& l);
  103. };
  104. struct CStringHashPolicy
  105. {
  106. typedef const char* Lookup;
  107. static HashNumber hash(const Lookup& l);
  108. static bool match(const char* const& k, const Lookup& l);
  109. };
  110. // This file features many classes with numerous size_t fields, and each such
  111. // class has one or more methods that need to operate on all of these fields.
  112. // Writing these individually is error-prone -- it's easy to add a new field
  113. // without updating all the required methods. So we define a single macro list
  114. // in each class to name the fields (and notable characteristics of them), and
  115. // then use the following macros to transform those lists into the required
  116. // methods.
  117. //
  118. // - The |tabKind| value is used when measuring TabSizes.
  119. //
  120. // - The |servoKind| value is used when measuring ServoSizes and also for
  121. // the various sizeOfLiveGCThings() methods.
  122. //
  123. // In some classes, one or more of the macro arguments aren't used. We use '_'
  124. // for those.
  125. //
  126. #define DECL_SIZE(tabKind, servoKind, mSize) size_t mSize;
  127. #define ZERO_SIZE(tabKind, servoKind, mSize) mSize(0),
  128. #define COPY_OTHER_SIZE(tabKind, servoKind, mSize) mSize(other.mSize),
  129. #define ADD_OTHER_SIZE(tabKind, servoKind, mSize) mSize += other.mSize;
  130. #define SUB_OTHER_SIZE(tabKind, servoKind, mSize) \
  131. MOZ_ASSERT(mSize >= other.mSize); \
  132. mSize -= other.mSize;
  133. #define ADD_SIZE_TO_N(tabKind, servoKind, mSize) n += mSize;
  134. #define ADD_SIZE_TO_N_IF_LIVE_GC_THING(tabKind, servoKind, mSize) \
  135. /* Avoid self-comparison warnings by comparing enums indirectly. */ \
  136. n += (mozilla::IsSame<int[ServoSizes::servoKind], int[ServoSizes::GCHeapUsed]>::value) \
  137. ? mSize \
  138. : 0;
  139. #define ADD_TO_TAB_SIZES(tabKind, servoKind, mSize) sizes->add(JS::TabSizes::tabKind, mSize);
  140. #define ADD_TO_SERVO_SIZES(tabKind, servoKind, mSize) sizes->add(JS::ServoSizes::servoKind, mSize);
  141. } // namespace js
  142. namespace JS {
  143. struct ClassInfo
  144. {
  145. #define FOR_EACH_SIZE(macro) \
  146. macro(Objects, GCHeapUsed, objectsGCHeap) \
  147. macro(Objects, MallocHeap, objectsMallocHeapSlots) \
  148. macro(Objects, MallocHeap, objectsMallocHeapElementsNormal) \
  149. macro(Objects, MallocHeap, objectsMallocHeapElementsAsmJS) \
  150. macro(Objects, MallocHeap, objectsMallocHeapMisc) \
  151. macro(Objects, NonHeap, objectsNonHeapElementsNormal) \
  152. macro(Objects, NonHeap, objectsNonHeapElementsShared) \
  153. macro(Objects, NonHeap, objectsNonHeapElementsWasm) \
  154. macro(Objects, NonHeap, objectsNonHeapCodeWasm)
  155. ClassInfo()
  156. : FOR_EACH_SIZE(ZERO_SIZE)
  157. wasmGuardPages(0)
  158. {}
  159. void add(const ClassInfo& other) {
  160. FOR_EACH_SIZE(ADD_OTHER_SIZE)
  161. }
  162. void subtract(const ClassInfo& other) {
  163. FOR_EACH_SIZE(SUB_OTHER_SIZE)
  164. }
  165. size_t sizeOfAllThings() const {
  166. size_t n = 0;
  167. FOR_EACH_SIZE(ADD_SIZE_TO_N)
  168. return n;
  169. }
  170. bool isNotable() const {
  171. static const size_t NotabilityThreshold = 16 * 1024;
  172. return sizeOfAllThings() >= NotabilityThreshold;
  173. }
  174. size_t sizeOfLiveGCThings() const {
  175. size_t n = 0;
  176. FOR_EACH_SIZE(ADD_SIZE_TO_N_IF_LIVE_GC_THING)
  177. return n;
  178. }
  179. void addToTabSizes(TabSizes* sizes) const {
  180. FOR_EACH_SIZE(ADD_TO_TAB_SIZES)
  181. }
  182. void addToServoSizes(ServoSizes *sizes) const {
  183. FOR_EACH_SIZE(ADD_TO_SERVO_SIZES)
  184. }
  185. FOR_EACH_SIZE(DECL_SIZE)
  186. size_t wasmGuardPages;
  187. #undef FOR_EACH_SIZE
  188. };
  189. struct ShapeInfo
  190. {
  191. #define FOR_EACH_SIZE(macro) \
  192. macro(Other, GCHeapUsed, shapesGCHeapTree) \
  193. macro(Other, GCHeapUsed, shapesGCHeapDict) \
  194. macro(Other, GCHeapUsed, shapesGCHeapBase) \
  195. macro(Other, MallocHeap, shapesMallocHeapTreeTables) \
  196. macro(Other, MallocHeap, shapesMallocHeapDictTables) \
  197. macro(Other, MallocHeap, shapesMallocHeapTreeKids)
  198. ShapeInfo()
  199. : FOR_EACH_SIZE(ZERO_SIZE)
  200. dummy()
  201. {}
  202. void add(const ShapeInfo& other) {
  203. FOR_EACH_SIZE(ADD_OTHER_SIZE)
  204. }
  205. void subtract(const ShapeInfo& other) {
  206. FOR_EACH_SIZE(SUB_OTHER_SIZE)
  207. }
  208. size_t sizeOfAllThings() const {
  209. size_t n = 0;
  210. FOR_EACH_SIZE(ADD_SIZE_TO_N)
  211. return n;
  212. }
  213. size_t sizeOfLiveGCThings() const {
  214. size_t n = 0;
  215. FOR_EACH_SIZE(ADD_SIZE_TO_N_IF_LIVE_GC_THING)
  216. return n;
  217. }
  218. void addToTabSizes(TabSizes* sizes) const {
  219. FOR_EACH_SIZE(ADD_TO_TAB_SIZES)
  220. }
  221. void addToServoSizes(ServoSizes *sizes) const {
  222. FOR_EACH_SIZE(ADD_TO_SERVO_SIZES)
  223. }
  224. FOR_EACH_SIZE(DECL_SIZE)
  225. int dummy; // present just to absorb the trailing comma from FOR_EACH_SIZE(ZERO_SIZE)
  226. #undef FOR_EACH_SIZE
  227. };
  228. /**
  229. * Holds data about a notable class (one whose combined object and shape
  230. * instances use more than a certain amount of memory) so we can report it
  231. * individually.
  232. *
  233. * The only difference between this class and ClassInfo is that this class
  234. * holds a copy of the filename.
  235. */
  236. struct NotableClassInfo : public ClassInfo
  237. {
  238. NotableClassInfo();
  239. NotableClassInfo(const char* className, const ClassInfo& info);
  240. NotableClassInfo(NotableClassInfo&& info);
  241. NotableClassInfo& operator=(NotableClassInfo&& info);
  242. ~NotableClassInfo() {
  243. js_free(className_);
  244. }
  245. char* className_;
  246. private:
  247. NotableClassInfo(const NotableClassInfo& info) = delete;
  248. };
  249. /** Data for tracking JIT-code memory usage. */
  250. struct CodeSizes
  251. {
  252. #define FOR_EACH_SIZE(macro) \
  253. macro(_, NonHeap, ion) \
  254. macro(_, NonHeap, baseline) \
  255. macro(_, NonHeap, regexp) \
  256. macro(_, NonHeap, other) \
  257. macro(_, NonHeap, unused)
  258. CodeSizes()
  259. : FOR_EACH_SIZE(ZERO_SIZE)
  260. dummy()
  261. {}
  262. void addToServoSizes(ServoSizes *sizes) const {
  263. FOR_EACH_SIZE(ADD_TO_SERVO_SIZES)
  264. }
  265. FOR_EACH_SIZE(DECL_SIZE)
  266. int dummy; // present just to absorb the trailing comma from FOR_EACH_SIZE(ZERO_SIZE)
  267. #undef FOR_EACH_SIZE
  268. };
  269. /** Data for tracking GC memory usage. */
  270. struct GCSizes
  271. {
  272. // |nurseryDecommitted| is marked as NonHeap rather than GCHeapDecommitted
  273. // because we don't consider the nursery to be part of the GC heap.
  274. #define FOR_EACH_SIZE(macro) \
  275. macro(_, MallocHeap, marker) \
  276. macro(_, NonHeap, nurseryCommitted) \
  277. macro(_, MallocHeap, nurseryMallocedBuffers) \
  278. macro(_, MallocHeap, storeBufferVals) \
  279. macro(_, MallocHeap, storeBufferCells) \
  280. macro(_, MallocHeap, storeBufferSlots) \
  281. macro(_, MallocHeap, storeBufferWholeCells) \
  282. macro(_, MallocHeap, storeBufferGenerics)
  283. GCSizes()
  284. : FOR_EACH_SIZE(ZERO_SIZE)
  285. dummy()
  286. {}
  287. void addToServoSizes(ServoSizes *sizes) const {
  288. FOR_EACH_SIZE(ADD_TO_SERVO_SIZES)
  289. }
  290. FOR_EACH_SIZE(DECL_SIZE)
  291. int dummy; // present just to absorb the trailing comma from FOR_EACH_SIZE(ZERO_SIZE)
  292. #undef FOR_EACH_SIZE
  293. };
  294. /**
  295. * This class holds information about the memory taken up by identical copies of
  296. * a particular string. Multiple JSStrings may have their sizes aggregated
  297. * together into one StringInfo object. Note that two strings with identical
  298. * chars will not be aggregated together if one is a short string and the other
  299. * is not.
  300. */
  301. struct StringInfo
  302. {
  303. #define FOR_EACH_SIZE(macro) \
  304. macro(Strings, GCHeapUsed, gcHeapLatin1) \
  305. macro(Strings, GCHeapUsed, gcHeapTwoByte) \
  306. macro(Strings, MallocHeap, mallocHeapLatin1) \
  307. macro(Strings, MallocHeap, mallocHeapTwoByte)
  308. StringInfo()
  309. : FOR_EACH_SIZE(ZERO_SIZE)
  310. numCopies(0)
  311. {}
  312. void add(const StringInfo& other) {
  313. FOR_EACH_SIZE(ADD_OTHER_SIZE);
  314. numCopies++;
  315. }
  316. void subtract(const StringInfo& other) {
  317. FOR_EACH_SIZE(SUB_OTHER_SIZE);
  318. numCopies--;
  319. }
  320. bool isNotable() const {
  321. static const size_t NotabilityThreshold = 16 * 1024;
  322. size_t n = 0;
  323. FOR_EACH_SIZE(ADD_SIZE_TO_N)
  324. return n >= NotabilityThreshold;
  325. }
  326. size_t sizeOfLiveGCThings() const {
  327. size_t n = 0;
  328. FOR_EACH_SIZE(ADD_SIZE_TO_N_IF_LIVE_GC_THING)
  329. return n;
  330. }
  331. void addToTabSizes(TabSizes* sizes) const {
  332. FOR_EACH_SIZE(ADD_TO_TAB_SIZES)
  333. }
  334. void addToServoSizes(ServoSizes *sizes) const {
  335. FOR_EACH_SIZE(ADD_TO_SERVO_SIZES)
  336. }
  337. FOR_EACH_SIZE(DECL_SIZE)
  338. uint32_t numCopies; // How many copies of the string have we seen?
  339. #undef FOR_EACH_SIZE
  340. };
  341. /**
  342. * Holds data about a notable string (one which, counting all duplicates, uses
  343. * more than a certain amount of memory) so we can report it individually.
  344. *
  345. * The only difference between this class and StringInfo is that
  346. * NotableStringInfo holds a copy of some or all of the string's chars.
  347. */
  348. struct NotableStringInfo : public StringInfo
  349. {
  350. static const size_t MAX_SAVED_CHARS = 1024;
  351. NotableStringInfo();
  352. NotableStringInfo(JSString* str, const StringInfo& info);
  353. NotableStringInfo(NotableStringInfo&& info);
  354. NotableStringInfo& operator=(NotableStringInfo&& info);
  355. ~NotableStringInfo() {
  356. js_free(buffer);
  357. }
  358. char* buffer;
  359. size_t length;
  360. private:
  361. NotableStringInfo(const NotableStringInfo& info) = delete;
  362. };
  363. /**
  364. * This class holds information about the memory taken up by script sources
  365. * from a particular file.
  366. */
  367. struct ScriptSourceInfo
  368. {
  369. #define FOR_EACH_SIZE(macro) \
  370. macro(_, MallocHeap, misc)
  371. ScriptSourceInfo()
  372. : FOR_EACH_SIZE(ZERO_SIZE)
  373. numScripts(0)
  374. {}
  375. void add(const ScriptSourceInfo& other) {
  376. FOR_EACH_SIZE(ADD_OTHER_SIZE)
  377. numScripts++;
  378. }
  379. void subtract(const ScriptSourceInfo& other) {
  380. FOR_EACH_SIZE(SUB_OTHER_SIZE)
  381. numScripts--;
  382. }
  383. void addToServoSizes(ServoSizes *sizes) const {
  384. FOR_EACH_SIZE(ADD_TO_SERVO_SIZES)
  385. }
  386. bool isNotable() const {
  387. static const size_t NotabilityThreshold = 16 * 1024;
  388. size_t n = 0;
  389. FOR_EACH_SIZE(ADD_SIZE_TO_N)
  390. return n >= NotabilityThreshold;
  391. }
  392. FOR_EACH_SIZE(DECL_SIZE)
  393. uint32_t numScripts; // How many ScriptSources come from this file? (It
  394. // can be more than one in XML files that have
  395. // multiple scripts in CDATA sections.)
  396. #undef FOR_EACH_SIZE
  397. };
  398. /**
  399. * Holds data about a notable script source file (one whose combined
  400. * script sources use more than a certain amount of memory) so we can report it
  401. * individually.
  402. *
  403. * The only difference between this class and ScriptSourceInfo is that this
  404. * class holds a copy of the filename.
  405. */
  406. struct NotableScriptSourceInfo : public ScriptSourceInfo
  407. {
  408. NotableScriptSourceInfo();
  409. NotableScriptSourceInfo(const char* filename, const ScriptSourceInfo& info);
  410. NotableScriptSourceInfo(NotableScriptSourceInfo&& info);
  411. NotableScriptSourceInfo& operator=(NotableScriptSourceInfo&& info);
  412. ~NotableScriptSourceInfo() {
  413. js_free(filename_);
  414. }
  415. char* filename_;
  416. private:
  417. NotableScriptSourceInfo(const NotableScriptSourceInfo& info) = delete;
  418. };
  419. /**
  420. * These measurements relate directly to the JSRuntime, and not to zones and
  421. * compartments within it.
  422. */
  423. struct RuntimeSizes
  424. {
  425. #define FOR_EACH_SIZE(macro) \
  426. macro(_, MallocHeap, object) \
  427. macro(_, MallocHeap, atomsTable) \
  428. macro(_, MallocHeap, contexts) \
  429. macro(_, MallocHeap, temporary) \
  430. macro(_, MallocHeap, interpreterStack) \
  431. macro(_, MallocHeap, mathCache) \
  432. macro(_, MallocHeap, sharedImmutableStringsCache) \
  433. macro(_, MallocHeap, sharedIntlData) \
  434. macro(_, MallocHeap, uncompressedSourceCache) \
  435. macro(_, MallocHeap, scriptData)
  436. RuntimeSizes()
  437. : FOR_EACH_SIZE(ZERO_SIZE)
  438. scriptSourceInfo(),
  439. code(),
  440. gc(),
  441. notableScriptSources()
  442. {
  443. allScriptSources = js_new<ScriptSourcesHashMap>();
  444. if (!allScriptSources || !allScriptSources->init())
  445. MOZ_CRASH("oom");
  446. }
  447. ~RuntimeSizes() {
  448. // |allScriptSources| is usually deleted and set to nullptr before this
  449. // destructor runs. But there are failure cases due to OOMs that may
  450. // prevent that, so it doesn't hurt to try again here.
  451. js_delete(allScriptSources);
  452. }
  453. void addToServoSizes(ServoSizes *sizes) const {
  454. FOR_EACH_SIZE(ADD_TO_SERVO_SIZES)
  455. scriptSourceInfo.addToServoSizes(sizes);
  456. code.addToServoSizes(sizes);
  457. gc.addToServoSizes(sizes);
  458. }
  459. // The script source measurements in |scriptSourceInfo| are initially for
  460. // all script sources. At the end, if the measurement granularity is
  461. // FineGrained, we subtract the measurements of the notable script sources
  462. // and move them into |notableScriptSources|.
  463. FOR_EACH_SIZE(DECL_SIZE)
  464. ScriptSourceInfo scriptSourceInfo;
  465. CodeSizes code;
  466. GCSizes gc;
  467. typedef js::HashMap<const char*, ScriptSourceInfo,
  468. js::CStringHashPolicy,
  469. js::SystemAllocPolicy> ScriptSourcesHashMap;
  470. // |allScriptSources| is only used transiently. During the reporting phase
  471. // it is filled with info about every script source in the runtime. It's
  472. // then used to fill in |notableScriptSources| (which actually gets
  473. // reported), and immediately discarded afterwards.
  474. ScriptSourcesHashMap* allScriptSources;
  475. js::Vector<NotableScriptSourceInfo, 0, js::SystemAllocPolicy> notableScriptSources;
  476. #undef FOR_EACH_SIZE
  477. };
  478. struct UnusedGCThingSizes
  479. {
  480. #define FOR_EACH_SIZE(macro) \
  481. macro(Other, GCHeapUnused, object) \
  482. macro(Other, GCHeapUnused, script) \
  483. macro(Other, GCHeapUnused, lazyScript) \
  484. macro(Other, GCHeapUnused, shape) \
  485. macro(Other, GCHeapUnused, baseShape) \
  486. macro(Other, GCHeapUnused, objectGroup) \
  487. macro(Other, GCHeapUnused, string) \
  488. macro(Other, GCHeapUnused, symbol) \
  489. macro(Other, GCHeapUnused, jitcode) \
  490. macro(Other, GCHeapUnused, scope)
  491. UnusedGCThingSizes()
  492. : FOR_EACH_SIZE(ZERO_SIZE)
  493. dummy()
  494. {}
  495. UnusedGCThingSizes(UnusedGCThingSizes&& other)
  496. : FOR_EACH_SIZE(COPY_OTHER_SIZE)
  497. dummy()
  498. {}
  499. void addToKind(JS::TraceKind kind, intptr_t n) {
  500. switch (kind) {
  501. case JS::TraceKind::Object: object += n; break;
  502. case JS::TraceKind::String: string += n; break;
  503. case JS::TraceKind::Symbol: symbol += n; break;
  504. case JS::TraceKind::Script: script += n; break;
  505. case JS::TraceKind::Shape: shape += n; break;
  506. case JS::TraceKind::BaseShape: baseShape += n; break;
  507. case JS::TraceKind::JitCode: jitcode += n; break;
  508. case JS::TraceKind::LazyScript: lazyScript += n; break;
  509. case JS::TraceKind::ObjectGroup: objectGroup += n; break;
  510. case JS::TraceKind::Scope: scope += n; break;
  511. default:
  512. MOZ_CRASH("Bad trace kind for UnusedGCThingSizes");
  513. }
  514. }
  515. void addSizes(const UnusedGCThingSizes& other) {
  516. FOR_EACH_SIZE(ADD_OTHER_SIZE)
  517. }
  518. size_t totalSize() const {
  519. size_t n = 0;
  520. FOR_EACH_SIZE(ADD_SIZE_TO_N)
  521. return n;
  522. }
  523. void addToTabSizes(JS::TabSizes *sizes) const {
  524. FOR_EACH_SIZE(ADD_TO_TAB_SIZES)
  525. }
  526. void addToServoSizes(JS::ServoSizes *sizes) const {
  527. FOR_EACH_SIZE(ADD_TO_SERVO_SIZES)
  528. }
  529. FOR_EACH_SIZE(DECL_SIZE)
  530. int dummy; // present just to absorb the trailing comma from FOR_EACH_SIZE(ZERO_SIZE)
  531. #undef FOR_EACH_SIZE
  532. };
  533. struct ZoneStats
  534. {
  535. #define FOR_EACH_SIZE(macro) \
  536. macro(Other, GCHeapUsed, symbolsGCHeap) \
  537. macro(Other, GCHeapAdmin, gcHeapArenaAdmin) \
  538. macro(Other, GCHeapUsed, lazyScriptsGCHeap) \
  539. macro(Other, MallocHeap, lazyScriptsMallocHeap) \
  540. macro(Other, GCHeapUsed, jitCodesGCHeap) \
  541. macro(Other, GCHeapUsed, objectGroupsGCHeap) \
  542. macro(Other, MallocHeap, objectGroupsMallocHeap) \
  543. macro(Other, GCHeapUsed, scopesGCHeap) \
  544. macro(Other, MallocHeap, scopesMallocHeap) \
  545. macro(Other, MallocHeap, typePool) \
  546. macro(Other, MallocHeap, baselineStubsOptimized) \
  547. macro(Other, MallocHeap, uniqueIdMap) \
  548. macro(Other, MallocHeap, shapeTables)
  549. ZoneStats()
  550. : FOR_EACH_SIZE(ZERO_SIZE)
  551. unusedGCThings(),
  552. stringInfo(),
  553. shapeInfo(),
  554. extra(),
  555. allStrings(nullptr),
  556. notableStrings(),
  557. isTotals(true)
  558. {}
  559. ZoneStats(ZoneStats&& other)
  560. : FOR_EACH_SIZE(COPY_OTHER_SIZE)
  561. unusedGCThings(mozilla::Move(other.unusedGCThings)),
  562. stringInfo(mozilla::Move(other.stringInfo)),
  563. shapeInfo(mozilla::Move(other.shapeInfo)),
  564. extra(other.extra),
  565. allStrings(other.allStrings),
  566. notableStrings(mozilla::Move(other.notableStrings)),
  567. isTotals(other.isTotals)
  568. {
  569. other.allStrings = nullptr;
  570. MOZ_ASSERT(!other.isTotals);
  571. }
  572. ~ZoneStats() {
  573. // |allStrings| is usually deleted and set to nullptr before this
  574. // destructor runs. But there are failure cases due to OOMs that may
  575. // prevent that, so it doesn't hurt to try again here.
  576. js_delete(allStrings);
  577. }
  578. bool initStrings(JSRuntime* rt);
  579. void addSizes(const ZoneStats& other) {
  580. MOZ_ASSERT(isTotals);
  581. FOR_EACH_SIZE(ADD_OTHER_SIZE)
  582. unusedGCThings.addSizes(other.unusedGCThings);
  583. stringInfo.add(other.stringInfo);
  584. shapeInfo.add(other.shapeInfo);
  585. }
  586. size_t sizeOfLiveGCThings() const {
  587. MOZ_ASSERT(isTotals);
  588. size_t n = 0;
  589. FOR_EACH_SIZE(ADD_SIZE_TO_N_IF_LIVE_GC_THING)
  590. n += stringInfo.sizeOfLiveGCThings();
  591. n += shapeInfo.sizeOfLiveGCThings();
  592. return n;
  593. }
  594. void addToTabSizes(JS::TabSizes* sizes) const {
  595. MOZ_ASSERT(isTotals);
  596. FOR_EACH_SIZE(ADD_TO_TAB_SIZES)
  597. unusedGCThings.addToTabSizes(sizes);
  598. stringInfo.addToTabSizes(sizes);
  599. shapeInfo.addToTabSizes(sizes);
  600. }
  601. void addToServoSizes(JS::ServoSizes *sizes) const {
  602. MOZ_ASSERT(isTotals);
  603. FOR_EACH_SIZE(ADD_TO_SERVO_SIZES)
  604. unusedGCThings.addToServoSizes(sizes);
  605. stringInfo.addToServoSizes(sizes);
  606. shapeInfo.addToServoSizes(sizes);
  607. }
  608. // These string measurements are initially for all strings. At the end,
  609. // if the measurement granularity is FineGrained, we subtract the
  610. // measurements of the notable script sources and move them into
  611. // |notableStrings|.
  612. FOR_EACH_SIZE(DECL_SIZE)
  613. UnusedGCThingSizes unusedGCThings;
  614. StringInfo stringInfo;
  615. ShapeInfo shapeInfo;
  616. void* extra; // This field can be used by embedders.
  617. typedef js::HashMap<JSString*, StringInfo,
  618. js::InefficientNonFlatteningStringHashPolicy,
  619. js::SystemAllocPolicy> StringsHashMap;
  620. // |allStrings| is only used transiently. During the zone traversal it is
  621. // filled with info about every string in the zone. It's then used to fill
  622. // in |notableStrings| (which actually gets reported), and immediately
  623. // discarded afterwards.
  624. StringsHashMap* allStrings;
  625. js::Vector<NotableStringInfo, 0, js::SystemAllocPolicy> notableStrings;
  626. bool isTotals;
  627. #undef FOR_EACH_SIZE
  628. };
  629. struct CompartmentStats
  630. {
  631. // We assume that |objectsPrivate| is on the malloc heap, but it's not
  632. // actually guaranteed. But for Servo, at least, it's a moot point because
  633. // it doesn't provide an ObjectPrivateVisitor so the value will always be
  634. // zero.
  635. #define FOR_EACH_SIZE(macro) \
  636. macro(Private, MallocHeap, objectsPrivate) \
  637. macro(Other, GCHeapUsed, scriptsGCHeap) \
  638. macro(Other, MallocHeap, scriptsMallocHeapData) \
  639. macro(Other, MallocHeap, baselineData) \
  640. macro(Other, MallocHeap, baselineStubsFallback) \
  641. macro(Other, MallocHeap, ionData) \
  642. macro(Other, MallocHeap, typeInferenceTypeScripts) \
  643. macro(Other, MallocHeap, typeInferenceAllocationSiteTables) \
  644. macro(Other, MallocHeap, typeInferenceArrayTypeTables) \
  645. macro(Other, MallocHeap, typeInferenceObjectTypeTables) \
  646. macro(Other, MallocHeap, compartmentObject) \
  647. macro(Other, MallocHeap, compartmentTables) \
  648. macro(Other, MallocHeap, innerViewsTable) \
  649. macro(Other, MallocHeap, lazyArrayBuffersTable) \
  650. macro(Other, MallocHeap, objectMetadataTable) \
  651. macro(Other, MallocHeap, crossCompartmentWrappersTable) \
  652. macro(Other, MallocHeap, regexpCompartment) \
  653. macro(Other, MallocHeap, savedStacksSet) \
  654. macro(Other, MallocHeap, varNamesSet) \
  655. macro(Other, MallocHeap, nonSyntacticLexicalScopesTable) \
  656. macro(Other, MallocHeap, jitCompartment) \
  657. macro(Other, MallocHeap, privateData)
  658. CompartmentStats()
  659. : FOR_EACH_SIZE(ZERO_SIZE)
  660. classInfo(),
  661. extra(),
  662. allClasses(nullptr),
  663. notableClasses(),
  664. isTotals(true)
  665. {}
  666. CompartmentStats(CompartmentStats&& other)
  667. : FOR_EACH_SIZE(COPY_OTHER_SIZE)
  668. classInfo(mozilla::Move(other.classInfo)),
  669. extra(other.extra),
  670. allClasses(other.allClasses),
  671. notableClasses(mozilla::Move(other.notableClasses)),
  672. isTotals(other.isTotals)
  673. {
  674. other.allClasses = nullptr;
  675. MOZ_ASSERT(!other.isTotals);
  676. }
  677. CompartmentStats(const CompartmentStats&) = delete; // disallow copying
  678. ~CompartmentStats() {
  679. // |allClasses| is usually deleted and set to nullptr before this
  680. // destructor runs. But there are failure cases due to OOMs that may
  681. // prevent that, so it doesn't hurt to try again here.
  682. js_delete(allClasses);
  683. }
  684. bool initClasses(JSRuntime* rt);
  685. void addSizes(const CompartmentStats& other) {
  686. MOZ_ASSERT(isTotals);
  687. FOR_EACH_SIZE(ADD_OTHER_SIZE)
  688. classInfo.add(other.classInfo);
  689. }
  690. size_t sizeOfLiveGCThings() const {
  691. MOZ_ASSERT(isTotals);
  692. size_t n = 0;
  693. FOR_EACH_SIZE(ADD_SIZE_TO_N_IF_LIVE_GC_THING)
  694. n += classInfo.sizeOfLiveGCThings();
  695. return n;
  696. }
  697. void addToTabSizes(TabSizes* sizes) const {
  698. MOZ_ASSERT(isTotals);
  699. FOR_EACH_SIZE(ADD_TO_TAB_SIZES);
  700. classInfo.addToTabSizes(sizes);
  701. }
  702. void addToServoSizes(ServoSizes *sizes) const {
  703. MOZ_ASSERT(isTotals);
  704. FOR_EACH_SIZE(ADD_TO_SERVO_SIZES);
  705. classInfo.addToServoSizes(sizes);
  706. }
  707. // The class measurements in |classInfo| are initially for all classes. At
  708. // the end, if the measurement granularity is FineGrained, we subtract the
  709. // measurements of the notable classes and move them into |notableClasses|.
  710. FOR_EACH_SIZE(DECL_SIZE)
  711. ClassInfo classInfo;
  712. void* extra; // This field can be used by embedders.
  713. typedef js::HashMap<const char*, ClassInfo,
  714. js::CStringHashPolicy,
  715. js::SystemAllocPolicy> ClassesHashMap;
  716. // These are similar to |allStrings| and |notableStrings| in ZoneStats.
  717. ClassesHashMap* allClasses;
  718. js::Vector<NotableClassInfo, 0, js::SystemAllocPolicy> notableClasses;
  719. bool isTotals;
  720. #undef FOR_EACH_SIZE
  721. };
  722. typedef js::Vector<CompartmentStats, 0, js::SystemAllocPolicy> CompartmentStatsVector;
  723. typedef js::Vector<ZoneStats, 0, js::SystemAllocPolicy> ZoneStatsVector;
  724. struct RuntimeStats
  725. {
  726. // |gcHeapChunkTotal| is ignored because it's the sum of all the other
  727. // values. |gcHeapGCThings| is ignored because it's the sum of some of the
  728. // values from the zones and compartments. Both of those values are not
  729. // reported directly, but are just present for sanity-checking other
  730. // values.
  731. #define FOR_EACH_SIZE(macro) \
  732. macro(_, Ignore, gcHeapChunkTotal) \
  733. macro(_, GCHeapDecommitted, gcHeapDecommittedArenas) \
  734. macro(_, GCHeapUnused, gcHeapUnusedChunks) \
  735. macro(_, GCHeapUnused, gcHeapUnusedArenas) \
  736. macro(_, GCHeapAdmin, gcHeapChunkAdmin) \
  737. macro(_, Ignore, gcHeapGCThings)
  738. explicit RuntimeStats(mozilla::MallocSizeOf mallocSizeOf)
  739. : FOR_EACH_SIZE(ZERO_SIZE)
  740. runtime(),
  741. cTotals(),
  742. zTotals(),
  743. compartmentStatsVector(),
  744. zoneStatsVector(),
  745. currZoneStats(nullptr),
  746. mallocSizeOf_(mallocSizeOf)
  747. {}
  748. // Here's a useful breakdown of the GC heap.
  749. //
  750. // - rtStats.gcHeapChunkTotal
  751. // - decommitted bytes
  752. // - rtStats.gcHeapDecommittedArenas (decommitted arenas in non-empty chunks)
  753. // - unused bytes
  754. // - rtStats.gcHeapUnusedChunks (empty chunks)
  755. // - rtStats.gcHeapUnusedArenas (empty arenas within non-empty chunks)
  756. // - rtStats.zTotals.unusedGCThings.totalSize() (empty GC thing slots within non-empty arenas)
  757. // - used bytes
  758. // - rtStats.gcHeapChunkAdmin
  759. // - rtStats.zTotals.gcHeapArenaAdmin
  760. // - rtStats.gcHeapGCThings (in-use GC things)
  761. // == rtStats.zTotals.sizeOfLiveGCThings() + rtStats.cTotals.sizeOfLiveGCThings()
  762. //
  763. // It's possible that some arenas in empty chunks may be decommitted, but
  764. // we don't count those under rtStats.gcHeapDecommittedArenas because (a)
  765. // it's rare, and (b) this means that rtStats.gcHeapUnusedChunks is a
  766. // multiple of the chunk size, which is good.
  767. void addToServoSizes(ServoSizes *sizes) const {
  768. FOR_EACH_SIZE(ADD_TO_SERVO_SIZES)
  769. runtime.addToServoSizes(sizes);
  770. }
  771. FOR_EACH_SIZE(DECL_SIZE)
  772. RuntimeSizes runtime;
  773. CompartmentStats cTotals; // The sum of this runtime's compartments' measurements.
  774. ZoneStats zTotals; // The sum of this runtime's zones' measurements.
  775. CompartmentStatsVector compartmentStatsVector;
  776. ZoneStatsVector zoneStatsVector;
  777. ZoneStats* currZoneStats;
  778. mozilla::MallocSizeOf mallocSizeOf_;
  779. virtual void initExtraCompartmentStats(JSCompartment* c, CompartmentStats* cstats) = 0;
  780. virtual void initExtraZoneStats(JS::Zone* zone, ZoneStats* zstats) = 0;
  781. #undef FOR_EACH_SIZE
  782. };
  783. class ObjectPrivateVisitor
  784. {
  785. public:
  786. // Within CollectRuntimeStats, this method is called for each JS object
  787. // that has an nsISupports pointer.
  788. virtual size_t sizeOfIncludingThis(nsISupports* aSupports) = 0;
  789. // A callback that gets a JSObject's nsISupports pointer, if it has one.
  790. // Note: this function does *not* addref |iface|.
  791. typedef bool(*GetISupportsFun)(JSObject* obj, nsISupports** iface);
  792. GetISupportsFun getISupports_;
  793. explicit ObjectPrivateVisitor(GetISupportsFun getISupports)
  794. : getISupports_(getISupports)
  795. {}
  796. };
  797. extern JS_PUBLIC_API(bool)
  798. CollectRuntimeStats(JSContext* cx, RuntimeStats* rtStats, ObjectPrivateVisitor* opv, bool anonymize);
  799. extern JS_PUBLIC_API(size_t)
  800. SystemCompartmentCount(JSContext* cx);
  801. extern JS_PUBLIC_API(size_t)
  802. UserCompartmentCount(JSContext* cx);
  803. extern JS_PUBLIC_API(size_t)
  804. PeakSizeOfTemporary(const JSContext* cx);
  805. extern JS_PUBLIC_API(bool)
  806. AddSizeOfTab(JSContext* cx, JS::HandleObject obj, mozilla::MallocSizeOf mallocSizeOf,
  807. ObjectPrivateVisitor* opv, TabSizes* sizes);
  808. extern JS_PUBLIC_API(bool)
  809. AddServoSizeOf(JSContext* cx, mozilla::MallocSizeOf mallocSizeOf,
  810. ObjectPrivateVisitor *opv, ServoSizes *sizes);
  811. } // namespace JS
  812. #undef DECL_SIZE
  813. #undef ZERO_SIZE
  814. #undef COPY_OTHER_SIZE
  815. #undef ADD_OTHER_SIZE
  816. #undef SUB_OTHER_SIZE
  817. #undef ADD_SIZE_TO_N
  818. #undef ADD_SIZE_TO_N_IF_LIVE_GC_THING
  819. #undef ADD_TO_TAB_SIZES
  820. #endif /* js_MemoryMetrics_h */