4040
4141namespace JSC {
4242
43+ namespace {
44+
45+ class FileOutputStringBuilder {
46+ WTF_MAKE_NONCOPYABLE (FileOutputStringBuilder);
47+
48+ FileSystem::PlatformFileHandle m_fileHandle;
49+ StringBuilder m_builder;
50+ OverflowPolicy m_overflowPolicy;
51+ bool m_overflowed { false };
52+
53+ public:
54+ FileOutputStringBuilder (FileSystem::PlatformFileHandle fileHandle, OverflowPolicy overflowPolicy)
55+ : m_fileHandle(WTFMove(fileHandle))
56+ , m_builder(overflowPolicy)
57+ , m_overflowPolicy(overflowPolicy)
58+ {
59+ }
60+
61+ ~FileOutputStringBuilder ()
62+ {
63+ flush (true );
64+ }
65+
66+ template <typename ... StringTypes> void append (StringTypes... fragment)
67+ {
68+ m_builder.append (fragment...);
69+ flush ();
70+ }
71+
72+ void appendQuotedJSONString (const String& string)
73+ {
74+ m_builder.appendQuotedJSONString (string);
75+ flush ();
76+ }
77+
78+ void flush (bool force = false )
79+ {
80+ constexpr size_t kBufferLengthLimit = 4 * WTF::KB;
81+ if ((force && !m_builder.isEmpty ()) || m_builder.length () >= kBufferLengthLimit )
82+ {
83+ CString utf8String = m_builder.toStringPreserveCapacity ().utf8 ();
84+ auto ret = FileSystem::writeToFile (m_fileHandle, utf8String.span ());
85+
86+ if (ret < 0 )
87+ {
88+ // A negative return value does not necessarily mean we ran out of disk space, but due the
89+ // lack of a better indicator, we'll assume that is the case
90+ m_overflowed = true ;
91+
92+ if (m_overflowPolicy == OverflowPolicy::CrashOnOverflow)
93+ {
94+ WTFLogAlways (" Forcing crash (policy based) due lack of disk space in heap snapshot builder" );
95+ CRASH ();
96+ }
97+ }
98+
99+ m_builder.clear ();
100+ m_builder.reserveCapacity (kBufferLengthLimit );
101+ }
102+ }
103+
104+ bool hasOverflowed () const
105+ {
106+ return m_overflowed || m_builder.hasOverflowed ();
107+ }
108+
109+ void clear ()
110+ {
111+ m_builder.clear ();
112+ FileSystem::truncateFile (m_fileHandle, 0 );
113+ }
114+ };
115+
116+ } // namespace
117+
43118WTF_MAKE_TZONE_ALLOCATED_IMPL (HeapSnapshotBuilder);
44119
45120NodeIdentifier HeapSnapshotBuilder::nextAvailableObjectIdentifier = 1 ;
@@ -335,6 +410,20 @@ String HeapSnapshotBuilder::descriptionForCell(JSCell *cell) const
335410}
336411
337412String HeapSnapshotBuilder::json (Function<bool (const HeapSnapshotNode&)> allowNodeCallback)
413+ {
414+ StringBuilder json (m_overflowPolicy);
415+ writeJson (WTFMove (allowNodeCallback), json);
416+ return json.toString ();
417+ }
418+
419+ void HeapSnapshotBuilder::writeJsonToFile (FileSystem::PlatformFileHandle fileHandle)
420+ {
421+ FileOutputStringBuilder json (fileHandle, m_overflowPolicy);
422+ writeJson ([] (const HeapSnapshotNode&) { return true ; }, json);
423+ }
424+
425+ template <typename FileOutputStringBuilder>
426+ void HeapSnapshotBuilder::writeJson (Function<bool (const HeapSnapshotNode&)>&& allowNodeCallback, FileOutputStringBuilder &json)
338427{
339428 VM& vm = m_profiler.vm ();
340429 DeferGCForAWhile deferGC (vm);
@@ -356,8 +445,6 @@ String HeapSnapshotBuilder::json(Function<bool (const HeapSnapshotNode&)> allowN
356445 UncheckedKeyHashMap<UniquedStringImpl*, unsigned > edgeNameIndexes;
357446 unsigned nextEdgeNameIndex = 0 ;
358447
359- StringBuilder json (m_overflowPolicy);
360-
361448 auto appendNodeJSON = [&] (const HeapSnapshotNode& node) {
362449 // Let the client decide if they want to allow or disallow certain nodes.
363450 if (!allowNodeCallback (node))
@@ -633,9 +720,9 @@ String HeapSnapshotBuilder::json(Function<bool (const HeapSnapshotNode&)> allowN
633720 json.append (' }' );
634721 if (json.hasOverflowed ()) {
635722 m_hasOverflowed = true ;
636- return { };
723+
724+ json.clear ();
637725 }
638- return json.toString ();
639726}
640727
641728} // namespace JSC
0 commit comments