tmgraphnode *tmreader_component(tmreader *tmr, const char *name) { PLHashNumber hash; hash = PL_HashString(name); return (tmgraphnode*) *PL_HashTableRawLookup(tmr->components, hash, name); }
int tmreader_eventloop(tmreader *tmr, const char *filename, tmeventhandler eventhandler) { FILE *fp; char buf[NS_TRACE_MALLOC_MAGIC_SIZE]; tmevent event; static const char magic[] = NS_TRACE_MALLOC_MAGIC; if (strcmp(filename, "-") == 0) { fp = stdin; } else { #if defined(XP_WIN32) fp = fopen(filename, "rb"); #else fp = fopen(filename, "r"); #endif if (!fp) { fprintf(stderr, "%s: can't open %s: %s.\n", tmr->program, filename, strerror(errno)); return 0; } } if (read(fileno(fp), buf, sizeof buf) != sizeof buf || strncmp(buf, magic, sizeof buf) != 0) { fprintf(stderr, "%s: bad magic string %s at start of %s.\n", tmr->program, buf, filename); fprintf(stderr, "either the data file is out of date,\nor your tools are out of date.\n"); return 0; } /* Read in ticks per second. Used to convert platform specific intervals to time values */ if (read(fileno(fp), &tmr->ticksPerSec, sizeof tmr->ticksPerSec) != sizeof tmr->ticksPerSec) { fprintf(stderr, "%s: Cannot read ticksPerSec. Log file read error.\n", tmr->program); return 0; } tmr->ticksPerSec = PR_ntohl(tmr->ticksPerSec); #ifdef DEBUG_dp printf("DEBUG: ticks per sec = %d\n", tmr->ticksPerSec); #endif while (get_tmevent(fp, &event)) { switch (event.type) { case TM_EVENT_LIBRARY: { const void *key; PLHashNumber hash; PLHashEntry **hep, *he; key = (const void*) event.serial; hash = hash_serial(key); hep = PL_HashTableRawLookup(tmr->libraries, hash, key); he = *hep; PR_ASSERT(!he); if (he) exit(2); he = PL_HashTableRawAdd(tmr->libraries, hep, hash, key, event.u.libname); if (!he) { perror(tmr->program); return -1; } break; } case TM_EVENT_FILENAME: { const void *key; PLHashNumber hash; PLHashEntry **hep, *he; key = (const void*) event.serial; hash = hash_serial(key); hep = PL_HashTableRawLookup(tmr->filenames, hash, key); he = *hep; PR_ASSERT(!he); if (he) exit(2); he = PL_HashTableRawAdd(tmr->filenames, hep, hash, key, event.u.srcname); if (!he) { perror(tmr->program); return -1; } break; } case TM_EVENT_METHOD: { const void *key, *sourcekey; PLHashNumber hash, sourcehash; PLHashEntry **hep, *he, **sourcehep, *sourcehe; char *name, *head, *mark, save; tmgraphnode *comp, *lib; tmmethodnode *meth; key = (const void*) event.serial; hash = hash_serial(key); hep = PL_HashTableRawLookup(tmr->methods, hash, key); he = *hep; PR_ASSERT(!he); if (he) exit(2); name = event.u.method.name; he = PL_HashTableRawAdd(tmr->methods, hep, hash, key, name); if (!he) { perror(tmr->program); return -1; } meth = (tmmethodnode*) he; meth->linenumber = event.u.method.linenumber; sourcekey = (const void*)event.u.method.filename; sourcehash = hash_serial(sourcekey); sourcehep = PL_HashTableRawLookup(tmr->filenames, sourcehash, sourcekey); sourcehe = *sourcehep; meth->sourcefile = filename_name(sourcehe); head = name; mark = strchr(name, ':'); if (!mark) { mark = name; while (*mark != '\0' && *mark == '_') mark++; head = mark; mark = strchr(head, '_'); if (!mark) { mark = strchr(head, '+'); if (!mark) mark = head + strlen(head); } } save = *mark; *mark = '\0'; hash = PL_HashString(head); hep = PL_HashTableRawLookup(tmr->components, hash, head); he = *hep; if (he) { comp = (tmgraphnode*) he; } else { head = strdup(head); if (head) { he = PL_HashTableRawAdd(tmr->components, hep, hash, head, head); } if (!he) { perror(tmr->program); return -1; } comp = (tmgraphnode*) he; key = (const void*) event.u.method.library; hash = hash_serial(key); lib = (tmgraphnode*) *PL_HashTableRawLookup(tmr->libraries, hash, key); if (lib) { comp->up = lib; comp->next = lib->down; lib->down = comp; } } *mark = save; meth->graphnode.up = comp; meth->graphnode.next = comp->down; comp->down = &(meth->graphnode); break; } case TM_EVENT_CALLSITE: { const void *key, *mkey; PLHashNumber hash, mhash; PLHashEntry **hep, *he; tmcallsite *site, *parent; tmmethodnode *meth; key = (const void*) event.serial; hash = hash_serial(key); hep = PL_HashTableRawLookup(tmr->callsites, hash, key); he = *hep; /* there should not be an entry here! */ PR_ASSERT(!he); if (he) exit(2); if (event.u.site.parent == 0) { parent = &tmr->calltree_root; } else { parent = tmreader_callsite(tmr, event.u.site.parent); if (!parent) { fprintf(stderr, "%s: no parent for %lu (%lu)!\n", tmr->program, (unsigned long) event.serial, (unsigned long) event.u.site.parent); continue; } } he = PL_HashTableRawAdd(tmr->callsites, hep, hash, key, NULL); if (!he) { perror(tmr->program); return -1; } site = (tmcallsite*) he; site->parent = parent; site->siblings = parent->kids; parent->kids = site; site->kids = NULL; mkey = (const void*) event.u.site.method; mhash = hash_serial(mkey); meth = (tmmethodnode*) *PL_HashTableRawLookup(tmr->methods, mhash, mkey); site->method = meth; site->offset = event.u.site.offset; site->allocs.bytes.direct = site->allocs.bytes.total = 0; site->allocs.calls.direct = site->allocs.calls.total = 0; site->frees.bytes.direct = site->frees.bytes.total = 0; site->frees.calls.direct = site->frees.calls.total = 0; break; } case TM_EVENT_MALLOC: case TM_EVENT_CALLOC: case TM_EVENT_REALLOC: { tmcallsite *site; uint32 size, oldsize; double delta, sqdelta, sqszdelta = 0; tmgraphnode *comp, *lib; tmmethodnode *meth; site = tmreader_callsite(tmr, event.serial); if (!site) { fprintf(stderr, "%s: no callsite for '%c' (%lu)!\n", tmr->program, event.type, (unsigned long) event.serial); continue; } size = event.u.alloc.size; oldsize = event.u.alloc.oldsize; delta = (double)size - (double)oldsize; site->allocs.bytes.direct += (unsigned long)delta; if (event.type != TM_EVENT_REALLOC) site->allocs.calls.direct++; meth = site->method; if (meth) { meth->graphnode.allocs.bytes.direct += (unsigned long)delta; sqdelta = delta * delta; if (event.type == TM_EVENT_REALLOC) { sqszdelta = ((double)size * size) - ((double)oldsize * oldsize); meth->graphnode.sqsum += sqszdelta; } else { meth->graphnode.sqsum += sqdelta; meth->graphnode.allocs.calls.direct++; } comp = meth->graphnode.up; if (comp) { comp->allocs.bytes.direct += (unsigned long)delta; if (event.type == TM_EVENT_REALLOC) { comp->sqsum += sqszdelta; } else { comp->sqsum += sqdelta; comp->allocs.calls.direct++; } lib = comp->up; if (lib) { lib->allocs.bytes.direct += (unsigned long)delta; if (event.type == TM_EVENT_REALLOC) { lib->sqsum += sqszdelta; } else { lib->sqsum += sqdelta; lib->allocs.calls.direct++; } } } } break; } case TM_EVENT_FREE: { tmcallsite *site; uint32 size; tmgraphnode *comp, *lib; tmmethodnode *meth; site = tmreader_callsite(tmr, event.serial); if (!site) { fprintf(stderr, "%s: no callsite for '%c' (%lu)!\n", tmr->program, event.type, (unsigned long) event.serial); continue; } size = event.u.alloc.size; site->frees.bytes.direct += size; site->frees.calls.direct++; meth = site->method; if (meth) { meth->graphnode.frees.bytes.direct += size; meth->graphnode.frees.calls.direct++; comp = meth->graphnode.up; if (comp) { comp->frees.bytes.direct += size; comp->frees.calls.direct++; lib = comp->up; if (lib) { lib->frees.bytes.direct += size; lib->frees.calls.direct++; } } } break; } case TM_EVENT_STATS: break; } eventhandler(tmr, &event); } return 1; }
nsMsgGroupThread *nsMsgGroupView::AddHdrToThread(nsIMsgDBHdr *msgHdr, bool *pNewThread) { nsMsgKey msgKey; uint32_t msgFlags; msgHdr->GetMessageKey(&msgKey); msgHdr->GetFlags(&msgFlags); nsString hashKey; nsresult rv = HashHdr(msgHdr, hashKey); if (NS_FAILED(rv)) return nullptr; // if (m_sortType == nsMsgViewSortType::byDate) // msgKey = ((nsPRUint32Key *) hashKey)->GetValue(); nsCOMPtr<nsIMsgThread> msgThread; m_groupsTable.Get(hashKey, getter_AddRefs(msgThread)); bool newThread = !msgThread; *pNewThread = newThread; nsMsgViewIndex viewIndexOfThread; // index of first message in thread in view nsMsgViewIndex threadInsertIndex; // index of newly added header in thread nsMsgGroupThread *foundThread = static_cast<nsMsgGroupThread *>(msgThread.get()); if (foundThread) { // find the view index of the root node of the thread in the view viewIndexOfThread = GetIndexOfFirstDisplayedKeyInThread(foundThread, true); if (viewIndexOfThread == nsMsgViewIndex_None) { // Something is wrong with the group table. Remove the old group and // insert a new one. m_groupsTable.Remove(hashKey); foundThread = nullptr; *pNewThread = newThread = true; } } // If the thread does not already exist, create one if (!foundThread) { foundThread = CreateGroupThread(m_db); msgThread = do_QueryInterface(foundThread); m_groupsTable.Put(hashKey, msgThread); if (GroupViewUsesDummyRow()) { foundThread->m_dummy = true; msgFlags |= MSG_VIEW_FLAG_DUMMY | MSG_VIEW_FLAG_HASCHILDREN; } viewIndexOfThread = GetInsertIndex(msgHdr); if (viewIndexOfThread == nsMsgViewIndex_None) viewIndexOfThread = m_keys.Length(); // add the thread root node to the view InsertMsgHdrAt(viewIndexOfThread, msgHdr, msgKey, msgFlags | MSG_VIEW_FLAG_ISTHREAD | nsMsgMessageFlags::Elided, 0); // For dummy rows, Have the header serve as the dummy node (it will be added // again for its actual content later.) if (GroupViewUsesDummyRow()) foundThread->InsertMsgHdrAt(0, msgHdr); // Calculate the (integer thread key); this really only needs to be done for // the byDate case where the expanded state of the groups can be easily // persisted and restored because of the bounded, consecutive value space // occupied. We calculate an integer value in all cases mainly because // it's the sanest choice available... // (The thread key needs to be an integer, so parse hash keys that are // stringified integers to real integers, and hash actual strings into // integers.) if ((m_sortType == nsMsgViewSortType::byAttachments) || (m_sortType == nsMsgViewSortType::byFlagged) || (m_sortType == nsMsgViewSortType::byPriority) || (m_sortType == nsMsgViewSortType::byStatus) || (m_sortType == nsMsgViewSortType::byReceived) || (m_sortType == nsMsgViewSortType::byDate)) foundThread->m_threadKey = atoi(NS_LossyConvertUTF16toASCII(hashKey).get()); else foundThread->m_threadKey = (nsMsgKey) PL_HashString(NS_LossyConvertUTF16toASCII(hashKey).get()); } // Add the message to the thread as an actual content-bearing header. // (If we use dummy rows, it was already added to the thread during creation.) threadInsertIndex = foundThread->AddChildFromGroupView(msgHdr, this); // check if new hdr became thread root if (!newThread && threadInsertIndex == 0) { // update the root node's header (in the view) to be the same as the root // node in the thread. SetMsgHdrAt(msgHdr, viewIndexOfThread, msgKey, (msgFlags & ~(nsMsgMessageFlags::Elided)) | // maintain elided flag and dummy flag (m_flags[viewIndexOfThread] & (nsMsgMessageFlags::Elided | MSG_VIEW_FLAG_DUMMY)) // ensure thread and has-children flags are set | MSG_VIEW_FLAG_ISTHREAD | MSG_VIEW_FLAG_HASCHILDREN, 0); // update the content-bearing copy in the thread to match. (the root and // first nodes in the thread should always be the same header.) // note: the guy who used to be the root will still exist. If our list of // nodes was [A A], a new node B is introduced which sorts to be the first // node, giving us [B A A], our copy makes that [B B A], and things are // right in the world (since we want the first two headers to be the same // since one is our dummy and one is real.) if (GroupViewUsesDummyRow()) foundThread->SetMsgHdrAt(1, msgHdr); // replace the old duplicate dummy header. // we do not update the content-bearing copy in the view to match; we leave // that up to OnNewHeader, which is the piece of code who gets to care // about whether the thread's children are shown or not (elided) } return foundThread; }