blob: fa870affe36b4d6e64f032958f1953b0fe26ba1a [file] [log] [blame]
#include "traceloader.h"
#include "apitrace.h"
#include <QDebug>
#include <QFile>
#define FRAMES_TO_CACHE 100
static ApiTraceCall *
apiCallFromTraceCall(const trace::Call *call,
const QHash<QString, QUrl> &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<int>(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 = "<<function<<", url = "<<url.toString();
m_helpHash.insert(function, url);
}
} else {
qWarning() << "Couldn't open reference file "
<< file.fileName();
}
file.close();
}
void TraceLoader::scanTrace()
{
QList<ApiTraceFrame*> 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, request.useRegex)) {
unsigned frameIdx = callInFrame(call->no);
ApiTraceFrame *frame = m_createdFrames[frameIdx];
const QVector<ApiTraceCall*> 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<trace::Call*> 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<trace::Call*> &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, request.useRegex)) {
ApiTraceFrame *frame = m_createdFrames[frameIdx];
const QVector<ApiTraceCall*> 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,
bool useRegex)
{
/*
* FIXME: do string comparison directly on trace::Call
*/
ApiTraceCall *apiCall = apiCallFromTraceCall(call, m_helpHash,
0, 0, this);
bool result = apiCall->contains(str, sensitivity, useRegex);
delete apiCall;
return result;
}
QVector<ApiTraceCall*>
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<ApiTraceCall*>();
}
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<ApiTraceCall*> calls = fetchFrameContents(frame);
QVector<ApiTraceCall*>::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<ApiTraceCall*>
TraceLoader::FrameContents::topLevelCalls() const
{
return m_topLevelItems;
}
QVector<ApiTraceCall*>
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<QString, QUrl> 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"