2016-07-30 21:22:28 -05:00
|
|
|
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
|
|
|
|
/*
|
|
|
|
* This file is part of the LibreOffice project.
|
|
|
|
*
|
|
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <fstream>
|
2016-07-31 13:17:52 -05:00
|
|
|
#include <mutex>
|
2016-07-31 17:06:01 -05:00
|
|
|
#include <sstream>
|
2016-07-31 13:17:52 -05:00
|
|
|
#include <string>
|
2016-07-31 17:06:01 -05:00
|
|
|
#include <vector>
|
2016-07-30 21:22:28 -05:00
|
|
|
|
2016-08-06 20:23:57 -05:00
|
|
|
#include <Poco/DeflatingStream.h>
|
2016-08-17 18:41:58 -05:00
|
|
|
#include <Poco/InflatingStream.h>
|
2016-08-06 20:23:57 -05:00
|
|
|
|
2016-08-04 17:44:24 -05:00
|
|
|
#include "Util.hpp"
|
|
|
|
|
2016-07-30 21:22:28 -05:00
|
|
|
/// Dumps commands and notification trace.
|
2016-08-02 09:00:37 -05:00
|
|
|
class TraceFileRecord
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
enum class Direction : char
|
|
|
|
{
|
|
|
|
Invalid = 0,
|
|
|
|
Incoming = '>',
|
2016-08-02 20:09:01 -05:00
|
|
|
Outgoing = '<',
|
|
|
|
Event = '-'
|
2016-08-02 09:00:37 -05:00
|
|
|
};
|
|
|
|
|
|
|
|
TraceFileRecord() :
|
2016-09-28 03:20:24 -05:00
|
|
|
Dir(Direction::Invalid),
|
|
|
|
TimestampNs(0),
|
|
|
|
Pid(0)
|
2016-08-02 09:00:37 -05:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
Direction Dir;
|
|
|
|
unsigned TimestampNs;
|
2016-08-03 07:15:04 -05:00
|
|
|
unsigned Pid;
|
2016-08-04 09:31:11 -05:00
|
|
|
std::string SessionId;
|
2016-08-02 09:00:37 -05:00
|
|
|
std::string Payload;
|
|
|
|
};
|
|
|
|
|
2016-08-13 23:01:13 -05:00
|
|
|
/// Trace-file generator class.
|
|
|
|
/// Writes records into a trace file.
|
2016-07-31 12:56:57 -05:00
|
|
|
class TraceFileWriter
|
2016-07-30 21:22:28 -05:00
|
|
|
{
|
|
|
|
public:
|
2016-08-06 20:23:57 -05:00
|
|
|
TraceFileWriter(const std::string& path, const bool recordOugoing, const bool compress,
|
|
|
|
const std::vector<std::string>& filters) :
|
2016-07-30 22:06:12 -05:00
|
|
|
_epochStart(Poco::Timestamp().epochMicroseconds()),
|
2016-08-04 12:52:28 -05:00
|
|
|
_recordOutgoing(recordOugoing),
|
2016-08-06 20:23:57 -05:00
|
|
|
_compress(compress),
|
2016-08-04 17:44:24 -05:00
|
|
|
_filter(true),
|
2016-08-06 20:23:57 -05:00
|
|
|
_stream(path, compress ? std::ios::binary : std::ios::out),
|
2016-12-07 07:31:42 -06:00
|
|
|
_deflater(_stream, Poco::DeflatingStreamBuf::STREAM_GZIP)
|
2016-07-30 21:22:28 -05:00
|
|
|
{
|
2016-08-04 17:44:24 -05:00
|
|
|
for (const auto& f : filters)
|
|
|
|
{
|
|
|
|
_filter.deny(f);
|
|
|
|
}
|
2016-07-30 21:22:28 -05:00
|
|
|
}
|
|
|
|
|
2016-07-31 12:56:57 -05:00
|
|
|
~TraceFileWriter()
|
2016-07-30 21:22:28 -05:00
|
|
|
{
|
2016-09-28 04:39:20 -05:00
|
|
|
std::unique_lock<std::mutex> lock(_mutex);
|
|
|
|
|
2016-08-06 20:23:57 -05:00
|
|
|
_deflater.close();
|
2016-07-30 21:22:28 -05:00
|
|
|
_stream.close();
|
|
|
|
}
|
|
|
|
|
2016-08-02 20:09:01 -05:00
|
|
|
void writeEvent(const std::string& pId, const std::string& sessionId, const std::string& data)
|
|
|
|
{
|
2016-09-28 04:39:20 -05:00
|
|
|
std::unique_lock<std::mutex> lock(_mutex);
|
|
|
|
|
|
|
|
writeLocked(pId, sessionId, data, static_cast<char>(TraceFileRecord::Direction::Event));
|
|
|
|
flushLocked();
|
2016-08-02 20:09:01 -05:00
|
|
|
}
|
|
|
|
|
2016-08-03 07:15:04 -05:00
|
|
|
void writeIncoming(const std::string& pId, const std::string& sessionId, const std::string& data)
|
2016-07-30 21:22:28 -05:00
|
|
|
{
|
2016-09-28 04:39:20 -05:00
|
|
|
std::unique_lock<std::mutex> lock(_mutex);
|
|
|
|
|
2016-08-04 17:44:24 -05:00
|
|
|
if (_filter.match(data))
|
|
|
|
{
|
2016-09-28 04:39:20 -05:00
|
|
|
writeLocked(pId, sessionId, data, static_cast<char>(TraceFileRecord::Direction::Incoming));
|
2016-08-04 17:44:24 -05:00
|
|
|
}
|
2016-07-30 21:22:28 -05:00
|
|
|
}
|
|
|
|
|
2016-08-03 07:15:04 -05:00
|
|
|
void writeOutgoing(const std::string& pId, const std::string& sessionId, const std::string& data)
|
2016-08-02 09:00:37 -05:00
|
|
|
{
|
2016-09-28 04:39:20 -05:00
|
|
|
std::unique_lock<std::mutex> lock(_mutex);
|
|
|
|
|
2016-08-04 17:44:24 -05:00
|
|
|
if (_recordOutgoing && _filter.match(data))
|
2016-08-04 12:52:28 -05:00
|
|
|
{
|
2016-09-28 04:39:20 -05:00
|
|
|
writeLocked(pId, sessionId, data, static_cast<char>(TraceFileRecord::Direction::Outgoing));
|
2016-08-04 12:52:28 -05:00
|
|
|
}
|
2016-08-02 09:00:37 -05:00
|
|
|
}
|
|
|
|
|
2016-09-28 04:39:20 -05:00
|
|
|
private:
|
|
|
|
void flushLocked()
|
2016-09-19 17:48:30 -05:00
|
|
|
{
|
2016-09-28 04:39:20 -05:00
|
|
|
Util::assertIsLocked(_mutex);
|
|
|
|
|
2016-09-19 17:48:30 -05:00
|
|
|
_deflater.flush();
|
|
|
|
_stream.flush();
|
|
|
|
}
|
|
|
|
|
2016-09-28 04:39:20 -05:00
|
|
|
void writeLocked(const std::string& pId, const std::string& sessionId, const std::string& data, const char delim)
|
2016-07-31 06:54:47 -05:00
|
|
|
{
|
2016-09-28 04:39:20 -05:00
|
|
|
Util::assertIsLocked(_mutex);
|
|
|
|
|
2016-07-31 06:54:47 -05:00
|
|
|
const Poco::Int64 usec = Poco::Timestamp().epochMicroseconds() - _epochStart;
|
2016-08-06 20:23:57 -05:00
|
|
|
if (_compress)
|
|
|
|
{
|
|
|
|
_deflater.write(&delim, 1);
|
|
|
|
_deflater << usec;
|
|
|
|
_deflater.write(&delim, 1);
|
|
|
|
_deflater << pId;
|
|
|
|
_deflater.write(&delim, 1);
|
|
|
|
_deflater << sessionId;
|
|
|
|
_deflater.write(&delim, 1);
|
|
|
|
_deflater.write(data.c_str(), data.size());
|
|
|
|
_deflater.write("\n", 1);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
_stream.write(&delim, 1);
|
|
|
|
_stream << usec;
|
|
|
|
_stream.write(&delim, 1);
|
|
|
|
_stream << pId;
|
|
|
|
_stream.write(&delim, 1);
|
|
|
|
_stream << sessionId;
|
|
|
|
_stream.write(&delim, 1);
|
|
|
|
_stream.write(data.c_str(), data.size());
|
|
|
|
_stream.write("\n", 1);
|
|
|
|
}
|
2016-07-31 06:54:47 -05:00
|
|
|
}
|
|
|
|
|
2016-07-30 21:22:28 -05:00
|
|
|
private:
|
2016-07-30 22:06:12 -05:00
|
|
|
const Poco::Int64 _epochStart;
|
2016-08-04 12:52:28 -05:00
|
|
|
const bool _recordOutgoing;
|
2016-08-06 20:23:57 -05:00
|
|
|
const bool _compress;
|
2016-08-04 17:44:24 -05:00
|
|
|
Util::RegexListMatcher _filter;
|
2016-08-06 20:23:57 -05:00
|
|
|
std::ofstream _stream;
|
|
|
|
Poco::DeflatingOutputStream _deflater;
|
2016-07-30 22:06:12 -05:00
|
|
|
std::mutex _mutex;
|
2016-07-30 21:22:28 -05:00
|
|
|
};
|
|
|
|
|
2016-08-13 23:01:13 -05:00
|
|
|
/// Trace-file parser class.
|
|
|
|
/// Reads records from a trace file.
|
2016-07-31 13:17:52 -05:00
|
|
|
class TraceFileReader
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
TraceFileReader(const std::string& path) :
|
2016-08-17 18:41:58 -05:00
|
|
|
_compressed(path.size() > 2 && path.substr(path.size() - 2) == "gz"),
|
2016-08-04 11:17:22 -05:00
|
|
|
_epochStart(0),
|
2016-08-17 18:41:58 -05:00
|
|
|
_stream(path, _compressed ? std::ios::binary : std::ios::in),
|
|
|
|
_inflater(_stream, Poco::InflatingStreamBuf::STREAM_GZIP),
|
2016-08-04 09:31:11 -05:00
|
|
|
_index(0),
|
2016-07-31 18:32:37 -05:00
|
|
|
_indexIn(-1),
|
|
|
|
_indexOut(-1)
|
2016-07-31 13:17:52 -05:00
|
|
|
{
|
2016-07-31 18:32:37 -05:00
|
|
|
readFile();
|
2016-07-31 13:17:52 -05:00
|
|
|
}
|
|
|
|
|
2016-08-17 18:41:58 -05:00
|
|
|
~TraceFileReader()
|
|
|
|
{
|
|
|
|
_stream.close();
|
|
|
|
}
|
|
|
|
|
2016-08-04 11:17:22 -05:00
|
|
|
Poco::Int64 getEpoch() const { return _epochStart; }
|
|
|
|
|
2016-08-04 09:31:11 -05:00
|
|
|
TraceFileRecord getNextRecord()
|
|
|
|
{
|
|
|
|
if (_index < _records.size())
|
|
|
|
{
|
|
|
|
return _records[_index++];
|
|
|
|
}
|
|
|
|
|
|
|
|
// Invalid.
|
|
|
|
return TraceFileRecord();
|
|
|
|
}
|
2016-08-01 20:53:09 -05:00
|
|
|
|
2016-07-31 18:32:37 -05:00
|
|
|
TraceFileRecord getNextRecord(const TraceFileRecord::Direction dir)
|
|
|
|
{
|
|
|
|
if (dir == TraceFileRecord::Direction::Incoming)
|
|
|
|
{
|
|
|
|
if (_indexIn < _records.size())
|
|
|
|
{
|
|
|
|
const TraceFileRecord rec = _records[_indexIn];
|
|
|
|
_indexIn = advance(_indexIn, dir);
|
|
|
|
return rec;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (_indexOut < _records.size())
|
|
|
|
{
|
|
|
|
const TraceFileRecord rec = _records[_indexOut];
|
|
|
|
_indexOut = advance(_indexOut, dir);
|
|
|
|
return rec;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Invalid.
|
|
|
|
return TraceFileRecord();
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
2016-07-31 17:06:01 -05:00
|
|
|
void readFile()
|
|
|
|
{
|
2016-07-31 18:32:37 -05:00
|
|
|
_records.clear();
|
|
|
|
|
2016-07-31 17:06:01 -05:00
|
|
|
std::string line;
|
2016-08-17 18:41:58 -05:00
|
|
|
for (;;)
|
2016-07-31 17:06:01 -05:00
|
|
|
{
|
2016-08-17 18:41:58 -05:00
|
|
|
if (_compressed)
|
|
|
|
{
|
|
|
|
std::getline(_inflater, line);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
std::getline(_stream, line);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (line.empty())
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2016-07-31 17:06:01 -05:00
|
|
|
const auto v = split(line, line[0]);
|
2016-08-04 09:31:11 -05:00
|
|
|
if (v.size() == 4)
|
2016-07-31 17:06:01 -05:00
|
|
|
{
|
|
|
|
TraceFileRecord rec;
|
2016-08-02 09:00:37 -05:00
|
|
|
rec.Dir = static_cast<TraceFileRecord::Direction>(line[0]);
|
2016-08-04 09:31:11 -05:00
|
|
|
unsigned index = 0;
|
|
|
|
rec.TimestampNs = std::atoi(v[index++].c_str());
|
|
|
|
rec.Pid = std::atoi(v[index++].c_str());
|
|
|
|
rec.SessionId = v[index++];
|
|
|
|
rec.Payload = v[index++];
|
2016-07-31 17:06:01 -05:00
|
|
|
_records.push_back(rec);
|
|
|
|
}
|
2016-08-04 09:31:11 -05:00
|
|
|
else
|
|
|
|
{
|
|
|
|
fprintf(stderr, "Invalid trace file record, expected 4 tokens. [%s]\n", line.c_str());
|
|
|
|
}
|
2016-07-31 17:06:01 -05:00
|
|
|
}
|
2016-07-31 18:32:37 -05:00
|
|
|
|
2016-08-04 09:31:11 -05:00
|
|
|
if (_records.empty() ||
|
|
|
|
_records[0].Dir != TraceFileRecord::Direction::Event ||
|
|
|
|
_records[0].Payload.find("NewSession") != 0)
|
2016-08-01 20:53:09 -05:00
|
|
|
{
|
2016-08-04 09:31:11 -05:00
|
|
|
fprintf(stderr, "Invalid trace file with %ld records. First record: %s\n", _records.size(),
|
|
|
|
_records.empty() ? "<empty>" : _records[0].Payload.c_str());
|
|
|
|
throw std::runtime_error("Invalid trace file.");
|
2016-08-01 20:53:09 -05:00
|
|
|
}
|
2016-08-04 11:17:22 -05:00
|
|
|
|
|
|
|
_indexIn = advance(-1, TraceFileRecord::Direction::Incoming);
|
|
|
|
_indexOut = advance(-1, TraceFileRecord::Direction::Outgoing);
|
|
|
|
|
|
|
|
_epochStart = _records[0].TimestampNs;
|
2016-07-31 17:06:01 -05:00
|
|
|
}
|
|
|
|
|
2016-07-31 18:32:37 -05:00
|
|
|
std::vector<std::string> split(const std::string& s, const char delim) const
|
2016-07-31 17:06:01 -05:00
|
|
|
{
|
|
|
|
std::stringstream ss(s);
|
|
|
|
std::string item;
|
|
|
|
std::vector<std::string> v;
|
|
|
|
while (std::getline(ss, item, delim))
|
|
|
|
{
|
|
|
|
if (!item.empty())
|
|
|
|
{
|
|
|
|
v.push_back(item);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return v;
|
|
|
|
}
|
|
|
|
|
2016-07-31 18:32:37 -05:00
|
|
|
unsigned advance(unsigned index, const TraceFileRecord::Direction dir)
|
|
|
|
{
|
|
|
|
while (++index < _records.size())
|
|
|
|
{
|
|
|
|
if (_records[index].Dir == dir)
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return index;
|
|
|
|
}
|
|
|
|
|
2016-07-31 13:17:52 -05:00
|
|
|
private:
|
2016-08-17 18:41:58 -05:00
|
|
|
const bool _compressed;
|
2016-08-04 11:17:22 -05:00
|
|
|
Poco::Int64 _epochStart;
|
2016-07-31 17:06:01 -05:00
|
|
|
std::ifstream _stream;
|
2016-08-17 18:41:58 -05:00
|
|
|
Poco::InflatingInputStream _inflater;
|
2016-07-31 17:06:01 -05:00
|
|
|
std::vector<TraceFileRecord> _records;
|
2016-08-04 09:31:11 -05:00
|
|
|
unsigned _index;
|
2016-07-31 18:32:37 -05:00
|
|
|
unsigned _indexIn;
|
|
|
|
unsigned _indexOut;
|
2016-07-31 13:17:52 -05:00
|
|
|
};
|
|
|
|
|
2016-07-30 21:22:28 -05:00
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|