Sat, 03 Jan 2015 20:18:00 +0100
Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.
1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 #include "mozilla/VisualEventTracer.h"
6 #include "mozilla/Monitor.h"
7 #include "mozilla/TimeStamp.h"
8 #include "nscore.h"
9 #include "prthread.h"
10 #include "prprf.h"
11 #include "prenv.h"
12 #include "plstr.h"
13 #include "nsThreadUtils.h"
15 namespace mozilla { namespace eventtracer {
17 #ifdef MOZ_VISUAL_EVENT_TRACER
19 namespace {
21 const uint32_t kBatchSize = 256;
22 const char kTypeChars[eventtracer::eLast] = {' ','N','S','W','E','D'};
24 // Flushing thread and records queue monitor
25 mozilla::Monitor * gMonitor = nullptr;
27 // gInitialized and gCapture can be accessed from multiple threads
28 // simultaneously without any locking. However, since they are only ever
29 // *set* from the main thread, the chance of races manifesting is small
30 // and unlikely to be a problem in practice.
31 bool gInitialized;
33 // Flag to allow capturing
34 bool gCapture;
36 // Time stamp of the epoch we have started to capture
37 mozilla::TimeStamp * gProfilerStart;
39 // Duration of the log to keep up to
40 mozilla::TimeDuration * gMaxBacklogTime;
43 // Record of a single event
44 class Record {
45 public:
46 Record()
47 : mType(::mozilla::eventtracer::eNone)
48 , mItem(nullptr)
49 , mText(nullptr)
50 , mText2(nullptr)
51 {
52 MOZ_COUNT_CTOR(Record);
53 }
55 Record& operator=(const Record & aOther)
56 {
57 mType = aOther.mType;
58 mTime = aOther.mTime;
59 mItem = aOther.mItem;
60 mText = PL_strdup(aOther.mText);
61 mText2 = aOther.mText2 ? PL_strdup(aOther.mText2) : nullptr;
62 return *this;
63 }
65 ~Record()
66 {
67 PL_strfree(mText2);
68 PL_strfree(mText);
69 MOZ_COUNT_DTOR(Record);
70 }
72 uint32_t mType;
73 TimeStamp mTime;
74 void * mItem;
75 char * mText;
76 char * mText2;
77 };
79 char * DupCurrentThreadName()
80 {
81 if (NS_IsMainThread())
82 return PL_strdup("Main Thread");
84 PRThread * currentThread = PR_GetCurrentThread();
85 const char * name = PR_GetThreadName(currentThread);
86 if (name)
87 return PL_strdup(name);
89 char buffer[128];
90 PR_snprintf(buffer, 127, "Nameless %p", currentThread);
92 return PL_strdup(buffer);
93 }
95 // An array of events, each thread keeps its own private instance
96 class RecordBatch {
97 public:
98 RecordBatch(size_t aLength = kBatchSize,
99 char * aThreadName = DupCurrentThreadName())
100 : mRecordsHead(new Record[aLength])
101 , mRecordsTail(mRecordsHead + aLength)
102 , mNextRecord(mRecordsHead)
103 , mNextBatch(nullptr)
104 , mThreadNameCopy(aThreadName)
105 , mClosed(false)
106 {
107 MOZ_COUNT_CTOR(RecordBatch);
108 }
110 ~RecordBatch()
111 {
112 delete [] mRecordsHead;
113 PL_strfree(mThreadNameCopy);
114 MOZ_COUNT_DTOR(RecordBatch);
115 }
117 void Close() { mClosed = true; }
119 size_t Length() const { return mNextRecord - mRecordsHead; }
120 bool CanBeDeleted(const TimeStamp& aUntil) const;
122 static RecordBatch * Register();
123 static void Close(void * data); // Registered on freeing thread data
124 static RecordBatch * Clone(RecordBatch * aLog, const TimeStamp& aSince);
125 static void Delete(RecordBatch * aLog);
127 static RecordBatch * CloneLog();
128 static void GCLog(const TimeStamp& aUntil);
129 static void DeleteLog();
131 Record * mRecordsHead;
132 Record * mRecordsTail;
133 Record * mNextRecord;
135 RecordBatch * mNextBatch;
136 char * mThreadNameCopy;
137 bool mClosed;
138 };
140 // Protected by gMonitor, accessed concurently
141 // Linked list of batches threads want to flush on disk
142 RecordBatch * gLogHead = nullptr;
143 RecordBatch * gLogTail = nullptr;
145 // Registers the batch in the linked list
146 // static
147 RecordBatch *
148 RecordBatch::Register()
149 {
150 MonitorAutoLock mon(*gMonitor);
152 if (!gInitialized)
153 return nullptr;
155 if (gLogHead)
156 RecordBatch::GCLog(TimeStamp::Now() - *gMaxBacklogTime);
158 RecordBatch * batch = new RecordBatch();
159 if (!gLogHead)
160 gLogHead = batch;
161 else // gLogTail is non-null
162 gLogTail->mNextBatch = batch;
163 gLogTail = batch;
165 mon.Notify();
166 return batch;
167 }
169 void
170 RecordBatch::Close(void * data)
171 {
172 RecordBatch * batch = static_cast<RecordBatch*>(data);
173 batch->Close();
174 }
176 // static
177 RecordBatch *
178 RecordBatch::Clone(RecordBatch * aOther, const TimeStamp& aSince)
179 {
180 if (!aOther)
181 return nullptr;
183 size_t length = aOther->Length();
184 size_t min = 0;
185 size_t max = length;
186 Record * record = nullptr;
188 // Binary search for record with time >= aSince
189 size_t i;
190 while (min < max) {
191 i = (max + min) / 2;
193 record = aOther->mRecordsHead + i;
194 if (record->mTime >= aSince)
195 max = i;
196 else
197 min = i+1;
198 }
199 i = (max + min) / 2;
201 // How many Record's to copy?
202 size_t toCopy = length - i;
203 if (!toCopy)
204 return RecordBatch::Clone(aOther->mNextBatch, aSince);
206 // Clone
207 RecordBatch * clone = new RecordBatch(toCopy, PL_strdup(aOther->mThreadNameCopy));
208 for (; i < length; ++i) {
209 record = aOther->mRecordsHead + i;
210 *clone->mNextRecord = *record;
211 ++clone->mNextRecord;
212 }
213 clone->mNextBatch = RecordBatch::Clone(aOther->mNextBatch, aSince);
215 return clone;
216 }
218 // static
219 void
220 RecordBatch::Delete(RecordBatch * aLog)
221 {
222 while (aLog) {
223 RecordBatch * batch = aLog;
224 aLog = aLog->mNextBatch;
225 delete batch;
226 }
227 }
229 // static
230 RecordBatch *
231 RecordBatch::CloneLog()
232 {
233 TimeStamp startEpoch = *gProfilerStart;
234 TimeStamp backlogEpoch = TimeStamp::Now() - *gMaxBacklogTime;
236 TimeStamp since = (startEpoch > backlogEpoch) ? startEpoch : backlogEpoch;
238 MonitorAutoLock mon(*gMonitor);
240 return RecordBatch::Clone(gLogHead, since);
241 }
243 // static
244 void
245 RecordBatch::GCLog(const TimeStamp& aUntil)
246 {
247 // Garbage collect all unreferenced and old batches
248 gMonitor->AssertCurrentThreadOwns();
250 RecordBatch *volatile * referer = &gLogHead;
251 gLogTail = nullptr;
253 RecordBatch * batch = *referer;
254 while (batch) {
255 if (batch->CanBeDeleted(aUntil)) {
256 // The batch is completed and thus unreferenced by the thread
257 // and the most recent record has time older then the time
258 // we want to save records for, hence delete it.
259 *referer = batch->mNextBatch;
260 delete batch;
261 batch = *referer;
262 }
263 else {
264 // We walk the whole list, so this will end up filled with
265 // the very last valid element of it.
266 gLogTail = batch;
267 // The current batch is active, examine the next in the list.
268 batch = batch->mNextBatch;
269 // When the next batch is found expired, we must extract it
270 // from the list, shift the referer.
271 referer = &((*referer)->mNextBatch);
272 }
273 }
274 }
276 // static
277 void
278 RecordBatch::DeleteLog()
279 {
280 RecordBatch * batch;
281 {
282 MonitorAutoLock mon(*gMonitor);
283 batch = gLogHead;
284 gLogHead = nullptr;
285 gLogTail = nullptr;
286 }
288 RecordBatch::Delete(batch);
289 }
291 bool
292 RecordBatch::CanBeDeleted(const TimeStamp& aUntil) const
293 {
294 if (mClosed) {
295 // This flag is set when a thread releases this batch as
296 // its private data. It happens when the list is full or
297 // when the thread ends its job. We must not delete this
298 // batch from memory while it's held by a thread.
300 if (!Length()) {
301 // There are no records, just get rid of this empty batch.
302 return true;
303 }
305 if ((mNextRecord-1)->mTime <= aUntil) {
306 // Is the last record older then the time we demand records
307 // for? If not, this batch has expired.
308 return true;
309 }
310 }
312 // Not all conditions to close the batch met, keep it.
313 return false;
314 }
316 // Helper class for filtering events by MOZ_PROFILING_EVENTS
317 class EventFilter
318 {
319 public:
320 static EventFilter * Build(const char * filterVar);
321 bool EventPasses(const char * eventName);
323 ~EventFilter()
324 {
325 delete mNext;
326 PL_strfree(mFilter);
327 MOZ_COUNT_DTOR(EventFilter);
328 }
330 private:
331 EventFilter(const char * eventName, EventFilter * next)
332 : mFilter(PL_strdup(eventName))
333 , mNext(next)
334 {
335 MOZ_COUNT_CTOR(EventFilter);
336 }
338 char * mFilter;
339 EventFilter * mNext;
340 };
342 // static
343 EventFilter *
344 EventFilter::Build(const char * filterVar)
345 {
346 if (!filterVar || !*filterVar)
347 return nullptr;
349 // Reads a comma serpatated list of events.
351 // Copied from nspr logging code (read of NSPR_LOG_MODULES)
352 char eventName[64];
353 int pos = 0, count, delta = 0;
355 // Read up to a comma or EOF -> get name of an event first in the list
356 count = sscanf(filterVar, "%63[^,]%n", eventName, &delta);
357 if (count == 0)
358 return nullptr;
360 pos = delta;
362 // Skip a comma, if present, accept spaces around it
363 count = sscanf(filterVar + pos, " , %n", &delta);
364 if (count != EOF)
365 pos += delta;
367 // eventName contains name of the first event in the list
368 // second argument recursively parses the rest of the list string and
369 // fills mNext of the just created EventFilter object chaining the objects
370 return new EventFilter(eventName, Build(filterVar + pos));
371 }
373 bool
374 EventFilter::EventPasses(const char * eventName)
375 {
376 if (!strcmp(eventName, mFilter))
377 return true;
379 if (mNext)
380 return mNext->EventPasses(eventName);
382 return false;
383 }
385 // State and control variables, initialized in Init() method, after it
386 // immutable and read concurently.
387 EventFilter * gEventFilter = nullptr;
388 unsigned gThreadPrivateIndex;
390 // static
391 bool CheckEventFilters(uint32_t aType, void * aItem, const char * aText)
392 {
393 if (!gEventFilter)
394 return true;
396 if (aType == eName)
397 return true;
399 return gEventFilter->EventPasses(aText);
400 }
402 } // anon namespace
404 #endif //MOZ_VISUAL_EVENT_TRACER
406 // static
407 void Init()
408 {
409 #ifdef MOZ_VISUAL_EVENT_TRACER
410 const char * logEvents = PR_GetEnv("MOZ_PROFILING_EVENTS");
411 if (logEvents && *logEvents)
412 gEventFilter = EventFilter::Build(logEvents);
414 PRStatus status = PR_NewThreadPrivateIndex(&gThreadPrivateIndex, &RecordBatch::Close);
415 if (status != PR_SUCCESS)
416 return;
418 gMonitor = new mozilla::Monitor("Profiler");
419 if (!gMonitor)
420 return;
422 gProfilerStart = new mozilla::TimeStamp();
423 gMaxBacklogTime = new mozilla::TimeDuration();
425 gInitialized = true;
426 #endif
427 }
429 // static
430 void Shutdown()
431 {
432 #ifdef MOZ_VISUAL_EVENT_TRACER
433 gCapture = false;
434 gInitialized = false;
436 RecordBatch::DeleteLog();
438 if (gMonitor) {
439 delete gMonitor;
440 gMonitor = nullptr;
441 }
443 if (gEventFilter) {
444 delete gEventFilter;
445 gEventFilter = nullptr;
446 }
448 if (gProfilerStart) {
449 delete gProfilerStart;
450 gProfilerStart = nullptr;
451 }
453 if (gMaxBacklogTime) {
454 delete gMaxBacklogTime;
455 gMaxBacklogTime = nullptr;
456 }
457 #endif
458 }
460 // static
461 void Mark(uint32_t aType, void * aItem, const char * aText, const char * aText2)
462 {
463 #ifdef MOZ_VISUAL_EVENT_TRACER
464 if (!gInitialized || !gCapture)
465 return;
467 if (aType == eNone)
468 return;
470 if (!CheckEventFilters(aType, aItem, aText)) // Events use just aText
471 return;
473 RecordBatch * threadLogPrivate = static_cast<RecordBatch *>(
474 PR_GetThreadPrivate(gThreadPrivateIndex));
475 if (!threadLogPrivate) {
476 threadLogPrivate = RecordBatch::Register();
477 if (!threadLogPrivate)
478 return;
480 PR_SetThreadPrivate(gThreadPrivateIndex, threadLogPrivate);
481 }
483 Record * record = threadLogPrivate->mNextRecord;
484 record->mType = aType;
485 record->mTime = mozilla::TimeStamp::Now();
486 record->mItem = aItem;
487 record->mText = PL_strdup(aText);
488 record->mText2 = aText2 ? PL_strdup(aText2) : nullptr;
490 ++threadLogPrivate->mNextRecord;
491 if (threadLogPrivate->mNextRecord == threadLogPrivate->mRecordsTail) {
492 // Calls RecordBatch::Close(threadLogPrivate) that marks
493 // the batch as OK to be deleted from memory when no longer needed.
494 PR_SetThreadPrivate(gThreadPrivateIndex, nullptr);
495 }
496 #endif
497 }
500 #ifdef MOZ_VISUAL_EVENT_TRACER
502 // The scriptable classes
504 class VisualEventTracerLog : public nsIVisualEventTracerLog
505 {
506 NS_DECL_ISUPPORTS
507 NS_DECL_NSIVISUALEVENTTRACERLOG
509 VisualEventTracerLog(RecordBatch* aBatch)
510 : mBatch(aBatch)
511 , mProfilerStart(*gProfilerStart)
512 {}
514 virtual ~VisualEventTracerLog();
516 protected:
517 RecordBatch * mBatch;
518 TimeStamp mProfilerStart;
519 };
521 NS_IMPL_ISUPPORTS(VisualEventTracerLog, nsIVisualEventTracerLog)
523 VisualEventTracerLog::~VisualEventTracerLog()
524 {
525 RecordBatch::Delete(mBatch);
526 }
528 NS_IMETHODIMP
529 VisualEventTracerLog::GetJSONString(nsACString & _retval)
530 {
531 nsCString buffer;
533 buffer.Assign(NS_LITERAL_CSTRING("{\n\"version\": 1,\n\"records\":[\n"));
535 RecordBatch * batch = mBatch;
536 while (batch) {
537 if (batch != mBatch) {
538 // This is not the first batch we are writting, add comma
539 buffer.Append(NS_LITERAL_CSTRING(",\n"));
540 }
542 buffer.Append(NS_LITERAL_CSTRING("{\"thread\":\""));
543 buffer.Append(batch->mThreadNameCopy);
544 buffer.Append(NS_LITERAL_CSTRING("\",\"log\":[\n"));
546 static const int kBufferSize = 2048;
547 char buf[kBufferSize];
549 for (Record * record = batch->mRecordsHead;
550 record < batch->mNextRecord;
551 ++record) {
553 // mType carries both type and flags, separate type
554 // as lower 16 bits and flags as higher 16 bits.
555 // The json format expects this separated.
556 uint32_t type = record->mType & 0xffffUL;
557 uint32_t flags = record->mType >> 16;
558 PR_snprintf(buf, kBufferSize,
559 "{\"e\":\"%c\",\"t\":%llu,\"f\":%d,\"i\":\"%p\",\"n\":\"%s%s\"}%s\n",
560 kTypeChars[type],
561 static_cast<uint64_t>((record->mTime - mProfilerStart).ToMilliseconds()),
562 flags,
563 record->mItem,
564 record->mText,
565 record->mText2 ? record->mText2 : "",
566 (record == batch->mNextRecord - 1) ? "" : ",");
568 buffer.Append(buf);
569 }
571 buffer.Append(NS_LITERAL_CSTRING("]}\n"));
573 RecordBatch * next = batch->mNextBatch;
574 batch = next;
575 }
577 buffer.Append(NS_LITERAL_CSTRING("]}\n"));
578 _retval.Assign(buffer);
580 return NS_OK;
581 }
583 nsresult
584 VisualEventTracerLog::WriteToProfilingFile()
585 {
586 const char* filename = PR_GetEnv("MOZ_TRACE_FILE");
587 if (!filename) {
588 return NS_OK;
589 }
591 PRFileDesc* fd = PR_Open(filename, PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE,
592 0644);
593 if (!fd) {
594 return NS_ERROR_FILE_ACCESS_DENIED;
595 }
597 nsCString json;
598 GetJSONString(json);
600 int32_t bytesWritten = PR_Write(fd, json.get(), json.Length());
601 PR_Close(fd);
603 if (bytesWritten < json.Length()) {
604 return NS_ERROR_UNEXPECTED;
605 }
607 return NS_OK;
608 }
610 NS_IMPL_ISUPPORTS(VisualEventTracer, nsIVisualEventTracer)
612 NS_IMETHODIMP
613 VisualEventTracer::Start(const uint32_t aMaxBacklogSeconds)
614 {
615 if (!gInitialized)
616 return NS_ERROR_UNEXPECTED;
618 if (gCapture) {
619 NS_WARNING("VisualEventTracer has already been started");
620 return NS_ERROR_ALREADY_INITIALIZED;
621 }
623 *gMaxBacklogTime = TimeDuration::FromMilliseconds(aMaxBacklogSeconds * 1000);
625 *gProfilerStart = mozilla::TimeStamp::Now();
626 {
627 MonitorAutoLock mon(*gMonitor);
628 RecordBatch::GCLog(*gProfilerStart);
629 }
630 gCapture = true;
632 MOZ_EVENT_TRACER_MARK(this, "trace::start");
634 return NS_OK;
635 }
637 NS_IMETHODIMP
638 VisualEventTracer::Stop()
639 {
640 if (!gInitialized)
641 return NS_ERROR_UNEXPECTED;
643 if (!gCapture) {
644 NS_WARNING("VisualEventTracer is not runing");
645 return NS_ERROR_NOT_INITIALIZED;
646 }
648 MOZ_EVENT_TRACER_MARK(this, "trace::stop");
650 gCapture = false;
652 nsresult rv = NS_OK;
653 if (PR_GetEnv("MOZ_TRACE_FILE")) {
654 nsCOMPtr<nsIVisualEventTracerLog> tracelog;
655 rv = Snapshot(getter_AddRefs(tracelog));
656 if (NS_SUCCEEDED(rv)) {
657 rv = tracelog->WriteToProfilingFile();
658 }
659 }
661 return rv;
662 }
664 NS_IMETHODIMP
665 VisualEventTracer::Snapshot(nsIVisualEventTracerLog ** _result)
666 {
667 if (!gInitialized)
668 return NS_ERROR_UNEXPECTED;
670 RecordBatch * batch = RecordBatch::CloneLog();
672 nsRefPtr<VisualEventTracerLog> log = new VisualEventTracerLog(batch);
673 log.forget(_result);
675 return NS_OK;
676 }
678 #endif
680 } // eventtracer
681 } // mozilla