#include "traceloader.h" #include "apitrace.h" #include #include #define FRAMES_TO_CACHE 100 static ApiTraceCall * apiCallFromTraceCall(const trace::Call *call, const QHash &helpHash, ApiTraceFrame *frame, ApiTraceCall *parentCall, TraceLoader *loader) { ApiTraceCall *apiCall; if (parentCall) apiCall = new ApiTraceCall(parentCall, loader, call); else apiCall = new ApiTraceCall(frame, loader, call); apiCall->setHelpUrl(helpHash.value(apiCall->name())); return apiCall; } TraceLoader::TraceLoader(QObject *parent) : QObject(parent) { } TraceLoader::~TraceLoader() { m_parser.close(); qDeleteAll(m_signatures); } void TraceLoader::loadTrace(const QString &filename) { if (m_helpHash.isEmpty()) { loadHelpFile(); } if (!m_frameBookmarks.isEmpty()) { qDeleteAll(m_signatures); m_signatures.clear(); m_frameBookmarks.clear(); m_createdFrames.clear(); m_parser.close(); } if (!m_parser.open(filename.toLatin1())) { qDebug() << "error: failed to open " << filename; return; } if (!m_parser.supportsOffsets()) { emit parseProblem( "This trace in compressed in a format that does not allow random seeking.\n" "Please repack the trace with `apitrace repack`." ); m_parser.close(); return; } emit startedParsing(); scanTrace(); emit guessedApi(static_cast(m_parser.api)); emit finishedParsing(); } void TraceLoader::loadFrame(ApiTraceFrame *currentFrame) { fetchFrameContents(currentFrame); } int TraceLoader::numberOfFrames() const { return m_frameBookmarks.size(); } int TraceLoader::numberOfCallsInFrame(int frameIdx) const { if (frameIdx >= m_frameBookmarks.size()) { return 0; } FrameBookmarks::const_iterator itr = m_frameBookmarks.find(frameIdx); return itr->numberOfCalls; } void TraceLoader::loadHelpFile() { QFile file(":/resources/glreference.tsv"); if (file.open(QIODevice::ReadOnly | QIODevice::Text)) { QString line; while (!file.atEnd()) { line = file.readLine(); QString function = line.section('\t', 0, 0).trimmed(); QUrl url = QUrl(line.section('\t', 1, 1).trimmed()); //qDebug()<<"function = "< frames; ApiTraceFrame *currentFrame = 0; trace::Call *call; trace::ParseBookmark startBookmark; int numOfFrames = 0; int numOfCalls = 0; int lastPercentReport = 0; m_parser.getBookmark(startBookmark); while ((call = m_parser.scan_call())) { ++numOfCalls; if (call->flags & trace::CALL_FLAG_END_FRAME) { FrameBookmark frameBookmark(startBookmark); frameBookmark.numberOfCalls = numOfCalls; currentFrame = new ApiTraceFrame(); currentFrame->number = numOfFrames; currentFrame->setNumChildren(numOfCalls); currentFrame->setLastCallIndex(call->no); frames.append(currentFrame); m_createdFrames.append(currentFrame); m_frameBookmarks[numOfFrames] = frameBookmark; ++numOfFrames; if (m_parser.percentRead() - lastPercentReport >= 5) { emit parsed(m_parser.percentRead()); lastPercentReport = m_parser.percentRead(); } m_parser.getBookmark(startBookmark); numOfCalls = 0; } delete call; } if (numOfCalls) { //trace::File::Bookmark endBookmark = m_parser.currentBookmark(); FrameBookmark frameBookmark(startBookmark); frameBookmark.numberOfCalls = numOfCalls; currentFrame = new ApiTraceFrame(); currentFrame->number = numOfFrames; currentFrame->setNumChildren(numOfCalls); frames.append(currentFrame); m_createdFrames.append(currentFrame); m_frameBookmarks[numOfFrames] = frameBookmark; ++numOfFrames; } emit parsed(100); emit framesLoaded(frames); } ApiTraceCallSignature * TraceLoader::signature(unsigned id) { if (id >= m_signatures.count()) { m_signatures.resize(id + 1); return NULL; } else { return m_signatures[id]; } } void TraceLoader::addSignature(unsigned id, ApiTraceCallSignature *signature) { m_signatures[id] = signature; } void TraceLoader::searchNext(const ApiTrace::SearchRequest &request) { Q_ASSERT(m_parser.supportsOffsets()); int startFrame = m_createdFrames.indexOf(request.frame); const FrameBookmark &frameBookmark = m_frameBookmarks[startFrame]; m_parser.setBookmark(frameBookmark.start); trace::Call *call = 0; while ((call = m_parser.parse_call())) { if (callContains(call, request.text, request.cs)) { unsigned frameIdx = callInFrame(call->no); ApiTraceFrame *frame = m_createdFrames[frameIdx]; const QVector calls = fetchFrameContents(frame); for (int i = 0; i < calls.count(); ++i) { if (calls[i]->index() == call->no) { emit searchResult(request, ApiTrace::SearchResult_Found, calls[i]); break; } } delete call; return; } delete call; } emit searchResult(request, ApiTrace::SearchResult_NotFound, 0); } void TraceLoader::searchPrev(const ApiTrace::SearchRequest &request) { Q_ASSERT(m_parser.supportsOffsets()); int startFrame = m_createdFrames.indexOf(request.frame); trace::Call *call = 0; QList frameCalls; int frameIdx = startFrame; const FrameBookmark &frameBookmark = m_frameBookmarks[frameIdx]; int numCallsToParse = frameBookmark.numberOfCalls; m_parser.setBookmark(frameBookmark.start); while ((call = m_parser.parse_call())) { frameCalls.append(call); --numCallsToParse; if (numCallsToParse == 0) { bool foundCall = searchCallsBackwards(frameCalls, frameIdx, request); qDeleteAll(frameCalls); frameCalls.clear(); if (foundCall) { return; } --frameIdx; if (frameIdx >= 0) { const FrameBookmark &frameBookmark = m_frameBookmarks[frameIdx]; m_parser.setBookmark(frameBookmark.start); numCallsToParse = frameBookmark.numberOfCalls; } } } emit searchResult(request, ApiTrace::SearchResult_NotFound, 0); } bool TraceLoader::searchCallsBackwards(const QList &calls, int frameIdx, const ApiTrace::SearchRequest &request) { for (int i = calls.count() - 1; i >= 0; --i) { trace::Call *call = calls[i]; if (callContains(call, request.text, request.cs)) { ApiTraceFrame *frame = m_createdFrames[frameIdx]; const QVector apiCalls = fetchFrameContents(frame); for (int i = 0; i < apiCalls.count(); ++i) { if (apiCalls[i]->index() == call->no) { emit searchResult(request, ApiTrace::SearchResult_Found, apiCalls[i]); break; } } return true; } } return false; } int TraceLoader::callInFrame(int callIdx) const { unsigned numCalls = 0; for (int frameIdx = 0; frameIdx < m_frameBookmarks.size(); ++frameIdx) { const FrameBookmark &frameBookmark = m_frameBookmarks[frameIdx]; unsigned firstCall = numCalls; unsigned endCall = numCalls + frameBookmark.numberOfCalls; if (firstCall <= callIdx && endCall > callIdx) { return frameIdx; } numCalls = endCall; } Q_ASSERT(!"call not in the trace"); return 0; } bool TraceLoader::callContains(trace::Call *call, const QString &str, Qt::CaseSensitivity sensitivity) { /* * FIXME: do string comparison directly on trace::Call */ ApiTraceCall *apiCall = apiCallFromTraceCall(call, m_helpHash, 0, 0, this); bool result = apiCall->contains(str, sensitivity); delete apiCall; return result; } QVector TraceLoader::fetchFrameContents(ApiTraceFrame *currentFrame) { Q_ASSERT(currentFrame); if (currentFrame->isLoaded()) { return currentFrame->calls(); } unsigned frameIdx = currentFrame->number; int numOfCalls = numberOfCallsInFrame(frameIdx); if (numOfCalls) { const FrameBookmark &frameBookmark = m_frameBookmarks[frameIdx]; m_parser.setBookmark(frameBookmark.start); FrameContents frameCalls(numOfCalls); frameCalls.load(this, currentFrame, m_helpHash, m_parser); if (frameCalls.topLevelCount() == frameCalls.allCallsCount()) { emit frameContentsLoaded(currentFrame, frameCalls.allCalls(), frameCalls.allCalls(), frameCalls.binaryDataSize()); } else { emit frameContentsLoaded(currentFrame, frameCalls.topLevelCalls(), frameCalls.allCalls(), frameCalls.binaryDataSize()); } return frameCalls.allCalls(); } return QVector(); } void TraceLoader::findFrameStart(ApiTraceFrame *frame) { if (!frame->isLoaded()) { loadFrame(frame); } emit foundFrameStart(frame); } void TraceLoader::findFrameEnd(ApiTraceFrame *frame) { if (!frame->isLoaded()) { loadFrame(frame); } emit foundFrameEnd(frame); } void TraceLoader::findCallIndex(int index) { int frameIdx = callInFrame(index); ApiTraceFrame *frame = m_createdFrames[frameIdx]; QVector calls = fetchFrameContents(frame); QVector::const_iterator itr; ApiTraceCall *call = 0; for (itr = calls.constBegin(); itr != calls.constEnd(); ++itr) { if ((*itr)->index() == index) { call = *itr; break; } } if (call) { emit foundCallIndex(call); } } void TraceLoader::search(const ApiTrace::SearchRequest &request) { if (request.direction == ApiTrace::SearchRequest::Next) { searchNext(request); } else { searchPrev(request); } } TraceLoader::FrameContents::FrameContents(int numOfCalls) : m_allCalls(numOfCalls), m_binaryDataSize(0), m_parsedCalls(0) {} void TraceLoader::FrameContents::reset() { m_groups.clear(); m_allCalls.clear(); m_topLevelItems.clear(); m_binaryDataSize = 0; } int TraceLoader::FrameContents::topLevelCount() const { return m_topLevelItems.count(); } int TraceLoader::FrameContents::allCallsCount() const { return m_allCalls.count(); } quint64 TraceLoader::FrameContents::binaryDataSize() const { return m_binaryDataSize; } QVector TraceLoader::FrameContents::topLevelCalls() const { return m_topLevelItems; } QVector TraceLoader::FrameContents::allCalls() const { return m_allCalls; } bool TraceLoader::FrameContents::isEmpty() { return (m_allCalls.count() == 0); } bool TraceLoader::FrameContents::load(TraceLoader *loader, ApiTraceFrame *currentFrame, QHash helpHash, trace::Parser &parser) { bool bEndFrameReached = false; int initNumOfCalls = m_allCalls.count(); trace::Call *call; ApiTraceCall *apiCall = NULL; while ((call = parser.parse_call())) { apiCall = apiCallFromTraceCall(call, helpHash, currentFrame, m_groups.isEmpty() ? 0 : m_groups.top(), loader); Q_ASSERT(apiCall); if (initNumOfCalls) { Q_ASSERT(m_parsedCalls < m_allCalls.size()); m_allCalls[m_parsedCalls++] = apiCall; } else { m_allCalls.append(apiCall); } if (m_groups.count() == 0) { m_topLevelItems.append(apiCall); } else { m_groups.top()->addChild(apiCall); } if (call->flags & trace::CALL_FLAG_MARKER_PUSH) { m_groups.push(apiCall); } else if (call->flags & trace::CALL_FLAG_MARKER_POP) { if (m_groups.count()) { m_groups.top()->finishedAddingChildren(); m_groups.pop(); } } if (apiCall->hasBinaryData()) { QByteArray data = apiCall->arguments()[apiCall->binaryDataIndex()]. toByteArray(); m_binaryDataSize += data.size(); } delete call; if (apiCall->flags() & trace::CALL_FLAG_END_FRAME) { bEndFrameReached = true; break; } } if (initNumOfCalls) { // There can be fewer parsed calls when call in different // threads cross the frame boundary Q_ASSERT(m_parsedCalls <= initNumOfCalls); Q_ASSERT(m_parsedCalls <= m_allCalls.size()); m_allCalls.resize(m_parsedCalls); Q_ASSERT(m_parsedCalls <= currentFrame->numChildrenToLoad()); } m_allCalls.squeeze(); m_topLevelItems.squeeze(); return bEndFrameReached; } #include "traceloader.moc"