Reading ftrace pipes with Boost::asio posix stream_descriptor - c++

I'm trying to build an application that reads from the ftrace pipes at the debug fs.
It seems that when trying to read asynchronously from trace_pipe or
trace_pipe_raw using boost::asio API, the events waiting in pipe are being
processed and printed to screen by the async_read handle, but new events that arrive after the program started don't trigger the async_read handle.
Running the sample code below, i'm getting a print of all events waiting in queue but i don't get any print for new events that arrive later.
The same sample works perfectly if i'm trying to read from manually created pipe using mkfifo but doesn't work for the ftrace pipes.
#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <string>
#include <iostream>
namespace asio = boost::asio;
#ifdef BOOST_ASIO_HAS_POSIX_STREAM_DESCRIPTOR
typedef asio::posix::stream_descriptor stream_descriptor;
#endif
class PipeReader
{
typedef std::shared_ptr<PipeReader> PipeReaderPtr;
typedef std::weak_ptr<PipeReader> PipeReaderWeakPtr;
public:
static PipeReaderWeakPtr Create(asio::io_service& io_service, const std::string& path);
void HandleRead(PipeReaderPtr me, const boost::system::error_code &error);
private:
PipeReader(asio::io_service& io_service, const std::string& path);
stream_descriptor m_pipe;
char buf[4096];
};
PipeReader::PipeReaderWeakPtr PipeReader::Create(asio::io_service& io_service, const std::string& path)
{
PipeReaderPtr ptr(new PipeReader(io_service, path));
ptr->m_pipe.async_read_some(boost::asio::buffer(ptr->buf),
boost::bind(&PipeReader::HandleRead,
ptr.get(),
ptr,
asio::placeholders::error));
return ptr;
}
PipeReader::PipeReader(asio::io_service& io_service, const std::string& path)
: m_pipe(io_service)
{
int dev = open(path.c_str(), O_RDWR);
if (dev == -1) {
std::cout << "failed to open path - " << path << std::endl;
}
else
{
m_pipe.assign(dev);
}
}
void PipeReader::HandleRead(PipeReaderPtr me, const boost::system::error_code &error)
{
if (!error) {
std::string str(me->buf);
std::cout << "got message: " << str << std::endl;
m_pipe.async_read_some(boost::asio::buffer(me->buf),
boost::bind(&PipeReader::HandleRead,
this,
me,
asio::placeholders::error));
}
else
{
std::cout << "got error - " << error.message() << std::endl;
}
}
int main()
{
boost::asio::io_service io_service;
boost::asio::io_service::work dummy(io_service);
PipeReader::Create(io_service, "/sys/kernel/debug/tracing/trace_pipe");
io_service.run();
return 0;
}

I found the problem. it was a bug in the implementation of ftrace that caused the epoll to hang.
The bug was fixed at kernel 3.16.
correspondence thread,
commit in git hub

Related

boost process running() and exit_code() thread safety

I am using boost::process::child and boost::process::async_pipe to start an application and read asynchronously (through the means of boost::asio) everything that app outputs on screen whenever this happens.
I want to check also if the application is alive by using child::running() method; if not running I'd like to read the exit code using child::exit_code.
This is very useful ESPECIALLY as it is a way to be notified about an application crashing or exiting unexpectedly (I could not find a better way); when the app exits the callback is called with boost::system::error_code set.
Do you know if I can use these two methods inside the callback called by async_pipe::async_read_some ?
In general the much more simple question would be if child::running() and child::exit_code() are thread safe (in both Windows and Linux).
namespace bp = boost::process;
char my_buffer[1024];
boost::asio::io_service io;
bp::async_pipe in_pipe(io);
void handle_pipe_read(const boost::system::error_code &ec, std::size_t bytes_transferred);
void schedule_read() {
in_pipe.async_read_some(
boost::asio::buffer(my_buffer),
boost::bind(&handle_pipe_read,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
void handle_pipe_read(
const boost::system::error_code &ec,
std::size_t bytes_transferred
)
{
// Q: Is this call possible? 'handle_pipe_read' can run in any thread
if(c->running())
std::cout << "I am alive" << std::endl;
else
std::cout << "EXIT CODE:" << c->exit_code() << std::endl;
if(ec) return; //app probably exit
// Do something with buffer and re-schedule
schedule_read();
}
int main() {
bp::child c("my_program_url", bp::std_out > in_pipe);
any_c = &c;
schedule_read();
io.run();
}
Since you only run the io_service::run() on the main thread, all completion handlers also run there. There's no threading.
Remember to pass the io_service to the child, and use the on_exit handler:
Live On Coliru
#include <boost/process.hpp>
//#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <system_error>
#include <utility>
#include <iostream>
namespace bp = boost::process;
char my_buffer[1024];
boost::asio::io_service io;
bp::async_pipe in_pipe(io);
void handle_pipe_read(const boost::system::error_code &ec, std::size_t bytes_transferred);
void schedule_read() {
in_pipe.async_read_some(
boost::asio::buffer(my_buffer),
boost::bind(&handle_pipe_read, _1, _2));
}
void handle_pipe_read(const boost::system::error_code &ec, std::size_t bytes_transferred) {
if (ec)
return; // app probably exit
// Do something with buffer and re-schedule
std::cout.write(my_buffer, bytes_transferred);
if (in_pipe.is_open())
schedule_read();
}
int main() {
bp::child c("/bin/ls", bp::std_out > in_pipe,
bp::on_exit([](int code, std::error_code ec) {
std::cout << "Child exited (" << code << "): " << ec.message() << std::endl;
in_pipe.close();
}), io);
schedule_read();
io.run();
std::cout << "Service done (" << c.exit_code() << ")" << std::endl;
}
Prints:
a.out
main.cpp
Child exited (0): Success
Service done (0)
The only solution that worked for me is the following
schedule a completion read
always check calling child::running()
on error_code set don't reschedule
When the pipe gets broken (because of a crash) the completion handler for a read has the boost::error_code argument set to true. Despite this I've seen cases where child::running() is false also when boost::error_code is NOT set.

How to do async read/write with Beast websockets

How can I do async write and read using websockets from the Beast library? I have tried to adapted the synchronous write/read example provided in the Beast documentation here, but the code below does not behave as expected.
I expected the following output :
*launch application*
Written data ...
Received data : Hello world!
*Ctrl-C*
Closing application ...
I got this :
*launch application*
*Ctrl-C*
Closing application ...
Code :
#include <beast/core/to_string.hpp>
#include <beast/websocket.hpp>
#include <boost/asio.hpp>
#include <iostream>
#include <string>
/// Block until SIGINT or SIGTERM is received.
void sig_wait(beast::websocket::stream<boost::asio::ip::tcp::socket&>& ws)
{
boost::asio::io_service ios;
boost::asio::signal_set signals(ios, SIGINT, SIGTERM);
signals.async_wait(
[&](boost::system::error_code const&, int)
{
ws.close(beast::websocket::close_code::normal);
std::cout << "Closing application ..." << std::endl;
});
ios.run();
}
int main(int argc, char *argv[])
{
// Normal boost::asio setup
std::string const host = "echo.websocket.org";
boost::asio::io_service ios;
boost::asio::ip::tcp::resolver r{ios};
boost::asio::ip::tcp::socket sock{ios};
boost::asio::ip::tcp::resolver::iterator iter (r.resolve(boost::asio::ip::tcp::resolver::query{host, "80"}));
boost::asio::connect(sock,iter);
// WebSocket connect and send message
beast::websocket::stream<boost::asio::ip::tcp::socket&> ws{sock};
ws.handshake(host, "/");
ws.async_write(boost::asio::buffer(std::string("Hello world!")),
[&](beast::error_code const&)
{
std::cout << "Written data ..." << '\n';
}
);
// Register handle for async_read
beast::streambuf sb;
beast::websocket::opcode op;
ws.async_read(op,sb,
[&](beast::error_code const&)
{
std::cout << "Received data : " << to_string(sb.data()) << '\n';
}
);
sig_wait(ws);
}
Side note: I am fairly new to the Boost library in general, so I may have gotten some of the basics wrong ...
You must call io_service::run(), that's the blocking call that will animate the io_service.
There are now asynchronous WebSocket client examples that you can study or copy: http://www.boost.org/doc/libs/develop/libs/beast/doc/html/beast/examples.html
Here is an asynchronous client example which calls io_service::run() from main:
http://www.boost.org/doc/libs/develop/libs/beast/example/websocket/client/async/websocket_client_async.cpp

Boost Asio - Why do my asynchronous operations not launch?

I recently met a problem with boost::asio asynchronous tasks. I want to return a pointer on an object listening to a port.
It works when I use the socket.read_some method but this method blocks my main and I want my MyClass::create method to return.
So I tried a async_read call but I saw that inside my read() method, no asynchronous tasks are launched. I tried to figure out what may cause the problem but see no solution to this issue.
Here is my code, here it's not with an async_read but with an async_wait, and the same problem appears, the timer is not launched.
Thanks for any help I might get.
The header file:
#ifndef MYCLASS_HPP
#define MYCLASS_HPP
#include <memory>
#include <boost/asio.hpp>
class MyClass
{
public:
MyClass(boost::asio::io_service& ios);
void read();
void read_handler(const boost::system::error_code& error);
static std::shared_ptr<MyClass> create(std:: string const & host, uint16_t port);
bool connect (std::string const & host, uint16_t port);
void connect_handler(const boost::system::error_code& error);
boost::asio::ip::tcp::socket m_socket;
bool m_flag;
std::vector<uint8_t> m_buffer;
};
#endif
Source file:
#include "MyClass.hpp"
#include <boost/bind.hpp>
MyClass::MyClass(boost::asio::io_service& ios)
:m_flag(false), m_socket(ios), m_buffer(20)
{
}
void MyClass::read_handler(const boost::system::error_code& er)
{
std::cout << "Timer waited 5 sec" << std::endl;
}
void MyClass::read()
{
boost::asio::deadline_timer t(m_socket.get_io_service(),boost::posix_time::seconds(5));
t.async_wait(boost::bind(&MyClass::read_handler,this,boost::asio::placeholders::error));
m_socket.get_io_service().run();//Should make the io_service wait for all asynchronous tasks to finish
std::cout << "This message should be displayed after the wait" << std::endl;
}
void MyClass::connect_handler(const boost::system::error_code& error)
{
if(!error)
{
std::cout << "Connection done" << std::endl;
m_flag = 1;
}
else
{
std::cout << "Error in connection: " << error.message() << std::endl;
}
}
//connect method
bool MyClass::connect(std::string const & host, uint16_t port)
{
boost::asio::ip::tcp::endpoint endpoint(boost::asio::ip::address::from_string(host),port);
m_socket.async_connect(endpoint,
boost::bind(&MyClass::connect_handler, this,
boost::asio::placeholders::error));
m_socket.get_io_service().run();//Wait async_connect and connect_handler to finish
if (m_flag == 0) return false;
else return true;
}
std::shared_ptr<MyClass> MyClass::create(std:: string const & host, uint16_t port)
{
boost::asio::io_service ios;
std::shared_ptr<MyClass> ptr(new MyClass(ios));
bool bol = ptr->connect(host, port);
ptr->read();
//while(1){}
if(bol == true)
{
//connection success, reading currently listening, pointer is returned to the user
return ptr;
}
else
{
//connection failure, pointer is still returned to the user but not listening as he's not connected
return ptr;
}
}
And my main:
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/asio.hpp>
#include "MyClass.hpp"
int main()
{
try
{
std::cout << "Creation of instance" << std::endl;
std::shared_ptr <MyClass> var = MyClass::create("127.0.0.1", 8301);
std::cout << "Instance created" << std::endl;
}
catch (std::exception& e)
{
std::cerr << e.what() << std::endl;
}
return 0;
}
I figured out how to solve my problem.
I had indeed problems with io_service being destroyed after "create" method, so the pointer returned in the main was not able to continue reading.
I had to call run() at one point to launch callbacks but i couldn't do it in the main, as I wanted the main to keep doing other things.
So I created a class launching a separated thread and containing an io_service. That thread is calling run() periodically. It was then added as an attribute to MyClass.
Now I have the call to "create" returning a pointer to MyClass who doesn't stop whatever asynchronous task was launched in MyClass.

Boost asio async operation bad file descriptor

I'm usig boost asio for an IRC bot, and one of my async operation results in a bad file descriptor. I tried to put the socket in a shared_ptr, but I still got the "Bad File Descriptor" error. I don't know whats wrong in it.
Here are the files, I omitted some of the functions from the cpp file. But I you want to read the full file, it's here on my Github.
The error happends in the _read function.
Thanks!
irc.hpp
#ifndef H_IRC
#define H_IRC
#include <vector>
#include <boost/asio.hpp>
#include <boost/tokenizer.hpp>
#include <boost/shared_ptr.hpp>
class Irc
{
public:
Irc(const std::string &server, const std::string &port, const std::function<void()> onConnect);
void connect();
void close();
void user(const std::string &username);
void user(const std::string &username, const std::string &hostname, const std::string &server, const std::string &realname);
void nick(std::string &nickname);
void join(const std::string &chan);
void part(const std::string &chan);
void privmsg(const std::string &to, const std::string &msg);
void command(const std::string &cmd, const std::string &msg);
void command(const std::string &cmd, const std::string &to, const std::string &msg);
void run();
private:
void _read(const boost::system::error_code &error);
void _send(std::string &message);
void _readHandler(const boost::tokenizer<boost::char_separator<char> > &tokenizer);
void _connectHandler(const boost::system::error_code &error);
void _pong(const std::string &ping);
std::string _server;
std::string _port;
std::string _chan;
std::vector<std::function<void (const boost::tokenizer<boost::char_separator<char> >&)>> _readHandlers;
std::function<void()> _onConnect;
boost::asio::streambuf _buffer;
boost::asio::io_service _ios;
boost::shared_ptr<boost::asio::ip::tcp::socket> _socket;
};
#endif
irc.cpp
#include "irc.hpp"
#include <iostream>
#include <boost/bind.hpp>
#include <boost/make_shared.hpp>
Irc::Irc(const std::string &server, const std::string &port, const std::function<void()> onConnect)
: _server(server), _port(port), _onConnect(onConnect),
_socket(boost::make_shared<boost::asio::ip::tcp::socket>(boost::ref(_ios)))
{
// Ping back handler
_readHandlers.push_back([this](const boost::tokenizer<boost::char_separator<char> > &tokenizer) {
std::vector<std::string> tokens(begin(tokenizer), end(tokenizer));
if(tokens[0].compare("PING") == 0)
_pong(tokens[1]);
});
}
void Irc::connect()
{
boost::asio::ip::tcp::resolver resolver(_ios);
boost::asio::ip::tcp::resolver::query query(_server, _port);
boost::asio::ip::tcp::resolver::iterator it = resolver.resolve(query);
boost::asio::ip::tcp::resolver::iterator end;
boost::system::error_code error = boost::asio::error::host_not_found;
while(it != end)
{
if(!error)
break;
std::cout << "Connecting to " << _server << " " << _port << std::endl;
boost::asio::async_connect(*_socket, it,
boost::bind(&Irc::_connectHandler, this, error)
);
it++;
if(error)
std::cout << "Error : " << error.message() << std::endl;
}
if(error)
std::cout << "Error connectinf to " << _server << " " << error.message() << std::endl;
else
std::cout << "Connection success" << std::endl;
}
void Irc::close()
{
_socket->close();
_ios.stop();
}
void Irc::run()
{
boost::asio::async_read_until(*_socket, _buffer, "\r\n",
boost::bind(&Irc::_read, this,
boost::asio::placeholders::error
)
);
_ios.run();
}
/*
* Private
*/
void Irc::_read(const boost::system::error_code &error)
{
if(error)
{
std::cerr << "Error in read : " << error.message() << std::endl;
}
else
{
std::string data(buffers_begin(_buffer.data()), buffers_begin(_buffer.data()) + _buffer.size());
std::cout << data << std::endl;
boost::char_separator<char> sep("!#:; ");
boost::tokenizer<boost::char_separator<char> > tokenizer(data, sep);
_readHandler(tokenizer);
boost::asio::async_read_until(*_socket, _buffer, "\r\n",
boost::bind(&Irc::_read, this,
boost::asio::placeholders::error
)
);
}
}
inline void Irc::_send(std::string &message)
{
boost::asio::write(*_socket, boost::asio::buffer(message + "\r\n"));
}
void Irc::_readHandler(const boost::tokenizer<boost::char_separator<char> > &tokenizer)
{
for(auto it : _readHandlers)
it(tokenizer);
}
void Irc::_connectHandler(const boost::system::error_code &error)
{
if(!error)
{
_onConnect();
}
}
connect is never called.
This causes the "bad file handle" error
Further Notes
Suddenly, _send uses synchronous asio::write. Why?
Error handling should probably be added there, too (catch or pass error_code& argument).
There's only one socket which never gets re-initialized or assigned. Embedding it into a shared pointer isn't changing anything¹.
This however is strange:
std::cout << "Connecting to " << _server << " " << _port << std::endl;
boost::asio::async_connect(*_socket, it,
boost::bind(&Irc::_connectHandler, this, error)
);
This does potentially many asynchronous connect operations on the same socket simultaneously. This is a data race and therefore Undefined Behaviour, see: documentation.
So you need to fix it to use several sockets or sequential.
Turns out, this is very simple: you're already using the free function version of async_connect:
http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/reference/async_connect/overload1.html
This function attempts to connect a socket to one of a sequence of endpoints. It does this by repeated calls to the socket's async_connect member function, once for each endpoint in the sequence, until a connection is successfully established.
So the fix is to just call it once.
Your bind doesn't use a placeholder, instead uses a hardcoded error!
boost::system::error_code error = boost::asio::error::host_not_found;
boost::asio::async_connect(_socket, it, boost::bind(&Irc::_connectHandler, this, error));
Needs to be more like
boost::asio::async_connect(_socket, it, boost::bind(&Irc::_connectHandler, this, boost::asio::placeholders::error()));
after handling incoming traffic, you need to consume the buffer contents, or it will infinitely repeat the same:
_readHandler(tokenizer);
_buffer.consume(_buffer.size());
Pull Request
https://github.com/Bl4ckb0ne/irc-boost/pull/1
Adds:
869c225 Use shared_ptr
9042c6d Add function level trace
50dee1b Revert shared_ptr and rename _onConnect(ed)
20475b9 Fixing the async_connect debacle
c6d8a2e Fixed channel handling and consistency join/part
6fd9242 Initiate `connect()` instead of read from `run()`
06a6c06 Do **not** assume contiguous buffer storage (UB)
090fe8c Consume handled input
68e5e8a Comment
All the above changed, I have successfully connected to an IRC channel and receiving mesages.
¹ (especially not unless you make sure something hangs on to an instance of the shared_ptr)

Permission refused when connecting to domain socket created by Boost.Asio

I'm trying to create a server that receives connections via domain sockets. I can start the server and I can see the socket being created on the filesystem. But whenever I try to connect to it via socat I get the following error:
2015/03/02 14:00:10 socat[62720] E connect(3, LEN=19 AF=1 "/var/tmp/rpc.sock", 19): Connection refused
This is my Asio code (only the .cpp files). Despite the post title I'm using the Boost-free version of Asio but I don't think that would be a problem.
namespace myapp {
DomainListener::DomainListener(const string& addr) : socket{this->service}, Listener{addr} {
remove(this->address.c_str());
stream_protocol::endpoint ep(this->address);
stream_protocol::acceptor acceptor(this->service, ep);
acceptor.async_accept(this->socket, ep, bind(&DomainListener::accept_callback, this, _1));
}
DomainListener::~DomainListener() {
this->service.stop();
remove(this->address.c_str());
}
void DomainListener::accept_callback(const error_code& ec) noexcept {
this->socket.async_read_some(asio::buffer(this->data), bind(&DomainListener::read_data, this, _1, _2));
}
void DomainListener::read_data(const error_code& ec, size_t length) noexcept {
//std::cerr << "AAA" << std::endl;
//std::cerr << this->data[0] << std::endl;
//std::cerr << "BBB" << std::endl;
}
}
Listener::Listener(const string& addr) : work{asio::io_service::work(this->service)} {
this->address = addr;
}
void Listener::listen() {
this->service.run();
}
Listener::~Listener() {
}
In the code that uses these classes I call listen() whenever I want to start listening to the socket for connections.
I've managed to get this to work with libuv and changed to Asio because I thought it would make for more readable code but I'm finding the documentation to be very ambiguous.
The issue is most likely the lifetime of the acceptor.
The acceptor is an automatic variable in the DomainListener constructor. When the DomainListener constructor completes, the acceptor is destroyed, causing the acceptor to close and cancel outstanding operations, such as the async_accept operations. Cancelled operations will be provided an error code of asio::error::operation_aborted and scheduled for deferred invocation within the io_service. Hence, there may not be an active listener when attempting to connect to the domain socket. For more details on the affects of IO object destruction, see this answer.
DomainListener::DomainListener(const string&) : /* ... */
{
// ...
stream_protocol::acceptor acceptor(...);
acceptor.async_accept(..., bind(accept_callback, ...));
} // acceptor destroyed, and accept_callback likely cancelled
To resolve this, consider extending the lifetime of the acceptor by making it a data member for DomainListener. Additionally, checking the error_code provided to asynchronous operations can provide more insight into the asynchronous call chains.
Here is a complete minimal example demonstrating using domain sockets with Asio.
#include <cstdio>
#include <iostream>
#include <boost/array.hpp>
#include <boost/asio.hpp>
#include <boost/bind.hpp>
/// #brief server demonstrates using domain sockets to accept
/// and read from a connection.
class server
{
public:
server(
boost::asio::io_service& io_service,
const std::string& file)
: io_service_(io_service),
acceptor_(io_service_,
boost::asio::local::stream_protocol::endpoint(file)),
client_(io_service_)
{
std::cout << "start accepting connection" << std::endl;
acceptor_.async_accept(client_,
boost::bind(&server::handle_accept, this,
boost::asio::placeholders::error));
}
private:
void handle_accept(const boost::system::error_code& error)
{
std::cout << "handle_accept: " << error.message() << std::endl;
if (error) return;
std::cout << "start reading" << std::endl;
client_.async_read_some(boost::asio::buffer(buffer_),
boost::bind(&server::handle_read, this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
void handle_read(
const boost::system::error_code& error,
std::size_t bytes_transferred)
{
std::cout << "handle_read: " << error.message() << std::endl;
if (error) return;
std::cout << "read: ";
std::cout.write(buffer_.begin(), bytes_transferred);
std::cout.flush();
}
private:
boost::asio::io_service& io_service_;
boost::asio::local::stream_protocol::acceptor acceptor_;
boost::asio::local::stream_protocol::socket client_;
std::array<char, 1024> buffer_;
};
int main(int argc, char* argv[])
{
if (argc != 2)
{
std::cerr << "Usage: <file>\n";
return 1;
}
// Remove file on startup and exit.
std::string file(argv[1]);
struct file_remover
{
file_remover(std::string file): file_(file) { std::remove(file.c_str()); }
~file_remover() { std::remove(file_.c_str()); }
std::string file_;
} remover(file);
// Create and run the server.
boost::asio::io_service io_service;
server s(io_service, file);
io_service.run();
}
Coliru does not have socat installed, so the following commands use OpenBSD netcat to write "asio domain socket example" to the domain socket:
export SOCKFILE=$PWD/example.sock
./a.out $SOCKFILE &
sleep 1
echo "asio domain socket example" | nc -U $SOCKFILE
Which outputs:
start accepting connection
handle_accept: Success
start reading
handle_read: Success
read: asio domain socket example