#872: Get rid of libstdc++ streams

This commit is contained in:
Andrei Pangin
2024-01-27 23:51:15 +00:00
parent a17529378b
commit 7643623628
8 changed files with 227 additions and 50 deletions
+2 -9
View File
@@ -3,8 +3,6 @@
* SPDX-License-Identifier: Apache-2.0
*/
#include <fstream>
#include <sstream>
#include "asprof.h"
#include "hooks.h"
#include "profiler.h"
@@ -33,22 +31,17 @@ DLLEXPORT asprof_error_t asprof_execute(const char* command, asprof_writer_t out
Log::open(args);
if (!args.hasOutputFile()) {
// FIXME: get rid of stream
std::ostringstream out;
CallbackWriter out(output_callback);
error = Profiler::instance()->runInternal(args, out);
if (!error) {
if (output_callback != NULL) {
output_callback(out.str().data(), out.str().size());
}
return NULL;
}
} else {
std::ofstream out(args.file(), std::ios::out | std::ios::trunc);
FileWriter out(args.file());
if (!out.is_open()) {
return asprof_error("Could not open output file");
}
error = Profiler::instance()->runInternal(args, out);
out.close();
if (!error) {
return NULL;
}
+8 -6
View File
@@ -111,7 +111,7 @@ Trie* FlameGraph::addChild(Trie* f, const char* name, FrameTypeId type, u64 valu
}
}
void FlameGraph::dump(std::ostream& out, bool tree) {
void FlameGraph::dump(Writer& out, bool tree) {
_name_order = new u32[_cpool.size() + 1]();
_mintotal = _minwidth == 0 && tree ? _root._total / 1000 : (u64)(_root._total * _minwidth / 100);
int depth = _root.depth(_mintotal, _name_order);
@@ -167,7 +167,7 @@ void FlameGraph::dump(std::ostream& out, bool tree) {
delete[] _name_order;
}
void FlameGraph::printFrame(std::ostream& out, u32 key, const Trie& f, int level, u64 x) {
void FlameGraph::printFrame(Writer& out, u32 key, const Trie& f, int level, u64 x) {
u32 name_and_type = _name_order[f.nameIndex(key)] << 3 | f.type(key);
bool has_extra_types = (f._inlined | f._c1_compiled | f._interpreted) &&
f._inlined < f._total && f._interpreted < f._total;
@@ -217,7 +217,7 @@ void FlameGraph::printFrame(std::ostream& out, u32 key, const Trie& f, int level
}
}
void FlameGraph::printTreeFrame(std::ostream& out, const Trie& f, int level, const char** names) {
void FlameGraph::printTreeFrame(Writer& out, const Trie& f, int level, const char** names) {
std::vector<Node> children;
children.reserve(f._children.size());
for (std::map<u32, Trie>::const_iterator it = f._children.begin(); it != f._children.end(); ++it) {
@@ -264,7 +264,7 @@ void FlameGraph::printTreeFrame(std::ostream& out, const Trie& f, int level, con
}
}
void FlameGraph::printCpool(std::ostream& out) {
void FlameGraph::printCpool(Writer& out) {
out << "'all'";
std::string prev;
@@ -282,7 +282,9 @@ void FlameGraph::printCpool(std::ostream& out) {
StringUtils::replace(s, '\\', "\\\\", 2);
StringUtils::replace(s, '\'', "\\'", 2);
out << ",\n'" << s << "'";
out << ",\n'";
out.write(s.data(), s.size());
out << "'";
}
}
@@ -290,7 +292,7 @@ void FlameGraph::printCpool(std::ostream& out) {
_cpool = std::map<std::string, u32>();
}
const char* FlameGraph::printTill(std::ostream& out, const char* data, const char* till) {
const char* FlameGraph::printTill(Writer& out, const char* data, const char* till) {
const char* pos = strstr(data, till);
out.write(data, pos - data);
return pos + strlen(till);
+6 -6
View File
@@ -6,12 +6,12 @@
#ifndef _FLAMEGRAPH_H
#define _FLAMEGRAPH_H
#include <iostream>
#include <map>
#include <string>
#include "arch.h"
#include "arguments.h"
#include "vmEntry.h"
#include "writer.h"
class Trie {
@@ -75,10 +75,10 @@ class FlameGraph {
u64 _last_x;
u64 _last_total;
void printFrame(std::ostream& out, u32 key, const Trie& f, int level, u64 x);
void printTreeFrame(std::ostream& out, const Trie& f, int level, const char** names);
void printCpool(std::ostream& out);
const char* printTill(std::ostream& out, const char* data, const char* till);
void printFrame(Writer& out, u32 key, const Trie& f, int level, u64 x);
void printTreeFrame(Writer& out, const Trie& f, int level, const char** names);
void printCpool(Writer& out);
const char* printTill(Writer& out, const char* data, const char* till);
public:
FlameGraph(const char* title, Counter counter, double minwidth, bool reverse) :
@@ -100,7 +100,7 @@ class FlameGraph {
Trie* addChild(Trie* f, const char* name, FrameTypeId type, u64 value);
void dump(std::ostream& out, bool tree);
void dump(Writer& out, bool tree);
};
#endif // _FLAMEGRAPH_H
+5 -7
View File
@@ -3,8 +3,6 @@
* SPDX-License-Identifier: Apache-2.0
*/
#include <fstream>
#include <sstream>
#include <errno.h>
#include <string.h>
#include "asprof.h"
@@ -71,23 +69,23 @@ Java_one_profiler_AsyncProfiler_execute0(JNIEnv* env, jobject unused, jstring co
Log::open(args);
if (!args.hasOutputFile()) {
std::ostringstream out;
BufferWriter out;
error = Profiler::instance()->runInternal(args, out);
if (!error) {
if (out.tellp() >= 0x3fffffff) {
out << '\0';
if (out.size() >= 0x3fffffff) {
throwNew(env, "java/lang/IllegalStateException", "Output exceeds string size limit");
return NULL;
}
return env->NewStringUTF(out.str().c_str());
return env->NewStringUTF(out.buf());
}
} else {
std::ofstream out(args.file(), std::ios::out | std::ios::trunc);
FileWriter out(args.file());
if (!out.is_open()) {
throwNew(env, "java/io/IOException", strerror(errno));
return NULL;
}
error = Profiler::instance()->runInternal(args, out);
out.close();
if (!error) {
return env->NewStringUTF("OK");
}
+11 -15
View File
@@ -4,7 +4,6 @@
*/
#include <algorithm>
#include <fstream>
#include <dlfcn.h>
#include <unistd.h>
#include <stdint.h>
@@ -1295,7 +1294,7 @@ Error Profiler::flushJfr() {
return Error::OK;
}
Error Profiler::dump(std::ostream& out, Arguments& args) {
Error Profiler::dump(Writer& out, Arguments& args) {
MutexLocker ml(_state_lock);
if (_state != IDLE && _state != RUNNING) {
return Error("Profiler has not started");
@@ -1333,7 +1332,7 @@ Error Profiler::dump(std::ostream& out, Arguments& args) {
return Error::OK;
}
void Profiler::printUsedMemory(std::ostream& out) {
void Profiler::printUsedMemory(Writer& out) {
size_t call_trace_storage = _call_trace_storage.usedMemory();
size_t dictionaries = _class_map.usedMemory() + _symbol_map.usedMemory() + _thread_filter.usedMemory() + _jfr.usedMemory();
@@ -1379,7 +1378,7 @@ void Profiler::switchThreadEvents(jvmtiEventMode mode) {
*
* <frame>;<frame>;...;<topmost frame> <count>
*/
void Profiler::dumpCollapsed(std::ostream& out, Arguments& args) {
void Profiler::dumpCollapsed(Writer& out, Arguments& args) {
FrameName fn(args, args._style | STYLE_NO_SEMICOLON, _epoch, _thread_names_lock, _thread_names);
char buf[32];
@@ -1406,7 +1405,7 @@ void Profiler::dumpCollapsed(std::ostream& out, Arguments& args) {
}
}
void Profiler::dumpFlameGraph(std::ostream& out, Arguments& args, bool tree) {
void Profiler::dumpFlameGraph(Writer& out, Arguments& args, bool tree) {
char title[64];
if (args._title == NULL) {
Engine* active_engine = activeEngine();
@@ -1466,7 +1465,7 @@ void Profiler::dumpFlameGraph(std::ostream& out, Arguments& args, bool tree) {
flamegraph.dump(out, tree);
}
void Profiler::dumpText(std::ostream& out, Arguments& args) {
void Profiler::dumpText(Writer& out, Arguments& args) {
FrameName fn(args, args._style | STYLE_DOTTED, _epoch, _thread_names_lock, _thread_names);
char buf[1024] = {0};
@@ -1656,7 +1655,7 @@ void Profiler::timerLoop(void* timer_id) {
}
}
Error Profiler::runInternal(Arguments& args, std::ostream& out) {
Error Profiler::runInternal(Arguments& args, Writer& out) {
switch (args._action) {
case ACTION_START:
case ACTION_RESUME: {
@@ -1734,7 +1733,6 @@ Error Profiler::runInternal(Arguments& args, std::ostream& out) {
}
case ACTION_VERSION:
out << PROFILER_VERSION;
out.flush();
break;
default:
break;
@@ -1744,17 +1742,16 @@ Error Profiler::runInternal(Arguments& args, std::ostream& out) {
Error Profiler::run(Arguments& args) {
if (!args.hasOutputFile()) {
return runInternal(args, std::cout);
FileWriter out(STDOUT_FILENO);
return runInternal(args, out);
} else {
// Open output file under the lock to avoid races with background timer
MutexLocker ml(_state_lock);
std::ofstream out(args.file(), std::ios::out | std::ios::trunc);
FileWriter out(args.file());
if (!out.is_open()) {
return Error("Could not open output file");
}
Error error = runInternal(args, out);
out.close();
return error;
return runInternal(args, out);
}
}
@@ -1767,12 +1764,11 @@ Error Profiler::restart(Arguments& args) {
}
if (args._file != NULL && args._output != OUTPUT_NONE && args._output != OUTPUT_JFR) {
std::ofstream out(args.file(), std::ios::out | std::ios::trunc);
FileWriter out(args.file());
if (!out.is_open()) {
return Error("Could not open output file");
}
error = dump(out, args);
out.close();
if (error) {
return error;
}
+8 -7
View File
@@ -6,8 +6,8 @@
#ifndef _PROFILER_H
#define _PROFILER_H
#include <iostream>
#include <map>
#include <string>
#include <time.h>
#include "arch.h"
#include "arguments.h"
@@ -23,6 +23,7 @@
#include "threadFilter.h"
#include "trap.h"
#include "vmEntry.h"
#include "writer.h"
const int MAX_NATIVE_FRAMES = 128;
@@ -145,9 +146,9 @@ class Profiler {
void lockAll();
void unlockAll();
void dumpCollapsed(std::ostream& out, Arguments& args);
void dumpFlameGraph(std::ostream& out, Arguments& args, bool tree);
void dumpText(std::ostream& out, Arguments& args);
void dumpCollapsed(Writer& out, Arguments& args);
void dumpFlameGraph(Writer& out, Arguments& args, bool tree);
void dumpText(Writer& out, Arguments& args);
static Profiler* const _instance;
@@ -189,15 +190,15 @@ class Profiler {
CodeCacheArray* nativeLibs() { return &_native_libs; }
Error run(Arguments& args);
Error runInternal(Arguments& args, std::ostream& out);
Error runInternal(Arguments& args, Writer& out);
Error restart(Arguments& args);
void shutdown(Arguments& args);
Error check(Arguments& args);
Error start(Arguments& args, bool reset);
Error stop(bool restart = false);
Error flushJfr();
Error dump(std::ostream& out, Arguments& args);
void printUsedMemory(std::ostream& out);
Error dump(Writer& out, Arguments& args);
void printUsedMemory(Writer& out);
void switchThreadEvents(jvmtiEventMode mode);
int convertNativeTrace(int native_frames, const void** callchain, ASGCT_CallFrame* frames);
u64 recordSample(void* ucontext, u64 counter, EventType event_type, Event* event);
+101
View File
@@ -0,0 +1,101 @@
/*
* Copyright The async-profiler authors
* SPDX-License-Identifier: Apache-2.0
*/
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "writer.h"
Writer& Writer::operator<<(char c) {
write(&c, 1);
return *this;
}
Writer& Writer::operator<<(const char* s) {
write(s, strlen(s));
return *this;
}
Writer& Writer::operator<<(int n) {
char buf[16];
write(buf, snprintf(buf, sizeof(buf), "%d", n));
return *this;
}
Writer& Writer::operator<<(long n) {
char buf[24];
write(buf, snprintf(buf, sizeof(buf), "%ld", n));
return *this;
}
FileWriter::FileWriter(const char* file_name) : _size(0) {
_fd = open(file_name, O_WRONLY | O_TRUNC | O_CREAT, 0644);
_buf = (char*)malloc(BUF_SIZE);
}
FileWriter::FileWriter(int fd) : _fd(fd), _size(0) {
_buf = (char*)malloc(BUF_SIZE);
}
FileWriter::~FileWriter() {
flush(_buf, _size);
free(_buf);
if (_fd > STDERR_FILENO) {
close(_fd);
}
}
void FileWriter::flush(const char* data, size_t len) {
while (len > 0) {
ssize_t bytes = ::write(_fd, data, len);
if (bytes < 0) {
_err = errno;
break;
}
data += bytes;
len -= (size_t)bytes;
}
}
void FileWriter::write(const char* data, size_t len) {
if (_size + len > BUF_SIZE) {
flush(_buf, _size);
_size = 0;
if (len > BUF_SIZE) {
flush(data, len);
return;
}
}
memcpy(_buf + _size, data, len);
_size += len;
}
BufferWriter::BufferWriter(size_t capacity) : _size(0), _capacity(capacity) {
_buf = (char*)malloc(capacity);
}
BufferWriter::~BufferWriter() {
free(_buf);
}
void BufferWriter::write(const char* data, size_t len) {
size_t new_size = _size + len;
if (new_size > _capacity) {
_capacity = new_size >= _capacity * 2 ? new_size : _capacity * 2;
_buf = (char*)realloc(_buf, _capacity);
}
memcpy(_buf + _size, data, len);
_size = new_size;
}
void CallbackWriter::write(const char* data, size_t len) {
if (_output_callback != NULL) {
_output_callback(data, len);
}
}
+86
View File
@@ -0,0 +1,86 @@
/*
* Copyright The async-profiler authors
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef _WRITER_H
#define _WRITER_H
#include "asprof.h"
class Writer {
protected:
int _err;
Writer() : _err(0) {
}
public:
Writer& operator<<(char c);
Writer& operator<<(const char* s);
Writer& operator<<(int n);
Writer& operator<<(long n);
bool good() const {
return _err == 0;
}
virtual void write(const char* data, size_t len) = 0;
};
class FileWriter : public Writer {
private:
int _fd;
char* _buf;
size_t _size;
enum { BUF_SIZE = 8192 };
void flush(const char* data, size_t len);
public:
FileWriter(const char* file_name);
FileWriter(int fd);
~FileWriter();
bool is_open() const {
return _fd >= 0;
}
virtual void write(const char* data, size_t len);
};
class BufferWriter : public Writer {
private:
char* _buf;
size_t _size;
size_t _capacity;
public:
BufferWriter(size_t capacity = 256);
~BufferWriter();
char* buf() const {
return _buf;
}
size_t size() const {
return _size;
}
virtual void write(const char* data, size_t len);
};
class CallbackWriter : public Writer {
private:
asprof_writer_t _output_callback;
public:
CallbackWriter(asprof_writer_t output_callback) : _output_callback(output_callback) {
}
virtual void write(const char* data, size_t len);
};
#endif // _WRITER_H