2#include <clues/AutoAttachedTracee.hxx>
3#include <clues/ChildTracee.hxx>
4#include <clues/Engine.hxx>
5#include <clues/EventConsumer.hxx>
6#include <clues/ForeignTracee.hxx>
7#include <clues/format.hxx>
8#include <clues/logger.hxx>
11#include <cosmos/error/ApiError.hxx>
12#include <cosmos/error/InternalError.hxx>
13#include <cosmos/io/ILogger.hxx>
14#include <cosmos/proc/process.hxx>
21 stop(cosmos::signal::KILL);
23 }
catch (
const std::exception &ex) {
24 LOG_WARN(
"Trying to stop remaining tracess in ~Engine():" << ex.what());
27 LOG_ERROR(
"Failed to cleanup Engine");
34 LOG_WARN(
"Unknown event for PID " << cosmos::to_integral(pair.first) <<
" left unprocessed");
39 const AttachThreads attach_threads,
const cosmos::ProcessID sibling) {
40 TraceePtr sibling_ptr;
42 sibling_ptr = it->second;
44 auto tracee = std::make_shared<ForeignTracee>(*
this, m_consumer, sibling_ptr);
45 tracee->configure(pid);
46 tracee->attach(follow_children, attach_threads);
52 auto tracee = std::make_shared<ChildTracee>(*
this, m_consumer);
53 tracee->create(cmdline);
54 tracee->attach(follow_children);
59void Engine::checkCleanupTracee(TraceeMap::iterator it) {
60 auto &tracee = *it->second;
63 if (!tracee.isChildProcess()) {
72void Engine::checkUnknownEvents() {
82 LOG_WARN(
"delayed handling of unknown event yielded unexpected decision");
88void Engine::handleNoChildren() {
90 auto &tracee = it->second;
103 << cosmos::to_integral(tracee->pid())
104 <<
" likely disappeared because of execve() in another thread");
106 LOG_WARN(
"Tracee " << cosmos::to_integral(tracee->pid())
107 <<
" suddenly lost?!");
115 checkCleanupTracee(it);
120 cosmos::ChildState data;
124 data = *cosmos::proc::wait(cosmos::WaitFlags{
125 cosmos::WaitFlag::WAIT_FOR_EXITED,
126 cosmos::WaitFlag::WAIT_FOR_STOPPED});
127 }
catch (
const cosmos::ApiError &ex) {
128 if (ex.errnum() == cosmos::Errno::NO_CHILD) {
137 if (
const auto decision = handleEvent(data); decision ==
Decision::RETRY) {
142 std::make_pair(data.child.pid, std::move(data)));
145 LOG_WARN(
"additional unknown trace event for PID " <<
146 cosmos::to_integral(data.child.pid));
149 LOG_WARN(
"received unknown trace event " << format::event(data));
159 Tracee &tracee = *it->second;
162 }
catch (
const cosmos::ApiError &ex) {
163 auto pid = cosmos::to_integral(tracee.pid());
164 if (ex.errnum() == cosmos::Errno::SEARCH) {
177 LOG_INFO(
"tracee " << pid <<
" disappeared");
180 LOG_ERROR(
"tracee " << pid <<
" handling process event failed: " << ex.what());
184 checkCleanupTracee(it);
185 checkUnknownEvents();
194 if (data.trapped() && data.signal->isPtraceEventStop()) {
195 const auto [_, event] = cosmos::ptrace::decode_event(*data.signal);
196 if (event == cosmos::ptrace::Event::EXEC) {
205 cosmos::Tracee ptrace{data.child.pid};
206 const auto former_pid = ptrace.getPIDEventMsg();
207 LOG_DEBUG(
"PID " << cosmos::to_integral(former_pid) <<
" issued execve(), but main thread is not traced. Trying to update records.");
208 if (tryUpdateTraceePID(former_pid, data.child.pid)) {
213 }
else if (event == cosmos::ptrace::Event::STOP) {
225 LOG_DEBUG(
"PID " << cosmos::to_integral(data.child.pid)
226 <<
" likely auto-attached tracee for which we didn't see the CLONE/[V]FORK event yet, storing event for later.");
234bool Engine::tryUpdateTraceePID(
const cosmos::ProcessID old_pid,
const cosmos::ProcessID new_pid) {
243 node.key() = new_pid;
251 auto &tracee = *it->second;
253 cosmos::signal::send(tracee.pid(), *signal);
257 if (!tracee.alive()) {
264 const cosmos::ptrace::Event event,
const SystemCall &sc) {
266 LOG_DEBUG(
"auto-attach for " << cosmos::to_integral(pid));
268 if (event != cosmos::ptrace::Event::VFORK_DONE) {
269 auto tracee = std::make_shared<AutoAttachedTracee>(
274 tracee->configure(pid, event, sc);
276 auto [it, _] =
m_tracees.insert({pid, tracee});
278 EventConsumer::StatusFlags flags;
282 m_consumer.newChildProcess(parent, *it->second, event, flags);
284 checkCleanupTracee(it);
289 m_consumer.vforkComplete(parent, it->second);
291 m_consumer.vforkComplete(parent,
nullptr);
357 auto &old_tracee = *it->second;
361 }
catch (
const cosmos::ApiError &err) {
363 if (err.errnum() != cosmos::Errno::SEARCH) {
368 auto ret = it->second;
TraceeMap m_tracees
Currently active tracees.
Decision
Different decisions what to do with ptrace events.
@ DONE
the event has been successfully processed.
@ STORE
store the event for later.
@ DROP
ignore/drop the event.
@ RETRY
retry processing the event.
Decision checkUnknownTraceeEvent(const cosmos::ChildState &data)
Check the given trace event if we can make sense of it.
TraceePtr handleSubstitution(const cosmos::ProcessID old_pid)
Invoked by a Tracee when multi-threaded execve() leads to substitution of a PID by another.
TraceePtr addTracee(const cosmos::ProcessID pid, const FollowChildren follow_children, const AttachThreads attach_threads, const cosmos::ProcessID sibling=cosmos::ProcessID::INVALID)
Add the given pid as tracee.
EventMap m_unknown_events
Unknown ptrace events stored for later processing.
cosmos::ProcessID m_newly_attached_pid
The PID of a newly auto-attached Tracee, if any.
virtual ~Engine()
Tear down any tracees.
void handleAutoAttach(Tracee &parent, const cosmos::ProcessID pid, const cosmos::ptrace::Event event, const SystemCall &sc)
Invoked by a Tracee once a new child process is auto-attached.
void stop(const std::optional< cosmos::Signal > signal)
Stop tracing any active tracees.
void trace()
Enter the tracing main loop and process tracing events.
@ CLONED_THREAD
used in newChildProcess() to indicate that a new thread has been created.
Access to System Call Data.
Base class for traced processes.
bool hasClonedThread() const
Returns whether the current/last seen system call was a clone() for a thread.
@ WAIT_FOR_EXITED
we've already seen PTHREAD_EVENT_EXIT but are still waiting for CLD_EXITED.
bool detach()
Attempt to detach the Tracee.
void processEvent(const cosmos::ChildState &data)
Process the given ptrace event.
@ DETACHED
we already detached from the tracee
@ DEAD
the tracee no longer exists.
virtual bool isChildProcess() const
Returns whether the tracee is a child process created by us.
cosmos::NamedBool< struct attach_threads_t, true > AttachThreads
A strong boolean type denoting whether to automatically all other threads of a process.
cosmos::NamedBool< struct follow_children_t, true > FollowChildren
A strong boolean type denoting whether to automatically attach to newly created child processes.