]> git.saurik.com Git - apple/system_cmds.git/blobdiff - KDBG/MachineProcess.hpp
system_cmds-643.30.1.tar.gz
[apple/system_cmds.git] / KDBG / MachineProcess.hpp
diff --git a/KDBG/MachineProcess.hpp b/KDBG/MachineProcess.hpp
new file mode 100644 (file)
index 0000000..4b52051
--- /dev/null
@@ -0,0 +1,173 @@
+//
+//  MachineProcess.hpp
+//  KDBG
+//
+//  Created by James McIlree on 10/26/12.
+//  Copyright (c) 2014 Apple. All rights reserved.
+//
+
+//
+// Process life cycle
+//
+// There are four ways a process can be created:
+//
+// 1) CreatedByPreviousMachineState
+//
+//      It is a carryover from a previous Machine state. This happens when a
+//    live trace creates a machine state that is a union of a previous state
+//    and new event data.
+//
+// 2) CreatedByThreadMap
+//
+//      It is a process that was running at the time the trace events were
+//    taken. The kernel provides this data.
+//
+// 3) CreatedByExecEvent
+//
+//      It is a process that "reused" an existing pid, and exec'd a new process
+//    in place. The Machine State will completely close down the old process
+//    and create a new one to track data for the newly exec'd process.
+//
+// 4) CreatedByForkExecEvent
+//
+//      An existing process "forked", creating a new pid, and then "exec'd".
+//    This is seen in trace events as a TRACE_DATA_NEWTHREAD with a pid that
+//    does not match the callers pid.
+//
+// There are also two catch-all processes, "Unknown", and "Kernel". The kernel
+// process contains all kernel only threads, and unknown contains threads that
+// are encountered without any previous identifying information.
+//
+
+enum class kMachineProcessFlag : std::uint32_t {
+    CreatedByPreviousMachineState      = 0x00000001,
+    CreatedByThreadMap                 = 0x00000002,
+    CreatedByForkExecEvent             = 0x00000004,
+    CreatedByExecEvent                 = 0x00000008,
+    IsForkExecInProgress               = 0x00000010,
+    IsExecInProgress                   = 0x00000020,
+    IsUnknownProcess                   = 0x00000040,
+    IsKernelProcess                    = 0x00000080,
+    IsExitBySyscall                    = 0x00000100,
+    IsExitByJetsam                     = 0x00000400,
+    IsExitByExec                       = 0x00000800,
+    IsTraceTerminated                  = 0x00001000
+};
+
+template <typename SIZE> class Machine;
+
+template <typename SIZE>
+class MachineProcess {
+  protected:
+    pid_t                              _pid;
+    char                               _name[20]; // Actual limit is 16, we round up for NULL terminator
+    AbsInterval                                _timespan; // This is set at trace termination, or in post_initialize if still live.
+    AbsTime                            _exit_initiated_timestamp;
+    std::vector<MachineThread<SIZE>*>  _threads_by_time;
+    uint32_t                           _flags;
+    int32_t                            _exit_status;
+    int32_t                            _apptype; // Unset == -1
+
+    //
+    // Mutable API
+    //
+
+    friend class Machine<SIZE>;
+
+    void set_flags(kMachineProcessFlag flags)                  { _flags |= (uint32_t)flags; }
+    void clear_flags(kMachineProcessFlag flags)                        { _flags &= ~(uint32_t)flags; }
+    bool is_flag_set(kMachineProcessFlag flag) const           { return (_flags & (uint32_t)flag) > 0; }
+
+    //
+    // Process exit lifecycle
+    //
+    // Processes start to exit / terminate when one of the following happens:
+    //
+    // syscall to proc exit
+    // jetsam causes a SIGKILL
+    // syscall to exec
+    //
+    // It may be that more than one of these events happen. For example, jetsam
+    // may cause a process to die via a SIGKILL.
+    //
+    // For the purposes of this API, only the first method of initiating exit
+    // is recorded. This includes the timestamp; if you ask for the exit timestamp
+    // you will get the timestamp for the first invocation of any of the exit
+    // paths.
+    //
+    // Once a process starts terminating, it will eventually reach the point
+    // where no futher events will ever be seen for that process. When the
+    // last thread in the process is marked as trace terminated, the process
+    // is marked as trace terminated.
+    //
+    // The definitive test for a process being entirely done is trace termination.
+    //
+    
+    //
+    // The exit code and conditions are a bit of a mess.
+    // All processes exit. This is triggered by the BSD_PROC_EXIT
+    // tracepoint. Some processes chose to exit, some are forced to
+    // exit by signals (SIGKILL, for example). Some processes are
+    // forced to exit by a mechanism that appears to be a signal but
+    // we want to track them separately (jetsam).
+    //
+    // The upshot of this is the exit code is stored in waitpid
+    // style. See waitpid(2) for the macros used to decode this.
+    //
+    void set_exit_by_syscall(AbsTime timestamp, int exit_status);
+    void set_exit_by_jetsam(AbsTime timestamp);
+    void set_exit_by_exec(AbsTime timestamp);
+    void set_trace_terminated(AbsTime timestamp); // Also sets last timestamp
+
+    void set_apptype(uint32_t apptype);
+    void set_apptype_from_trequested(uint32_t apptype);
+    void set_name(const char* name);
+
+    void add_thread(MachineThread<SIZE>* thread);
+
+    bool is_exec_in_progress() const                                   { return (_flags & (uint32_t)kMachineProcessFlag::IsExecInProgress) > 0; }
+    bool is_fork_exec_in_progress() const                              { return (_flags & (uint32_t)kMachineProcessFlag::IsForkExecInProgress) > 0; }
+
+    void clear_fork_exec_in_progress();
+    void clear_exec_in_progress();
+
+    // This is called after all events have been processed, to allow the
+    // threads to be sorted.
+    void post_initialize(AbsTime last_machine_timestamp);
+
+  public:
+    MachineProcess(pid_t pid,
+                  const char* name,
+                  AbsTime create_timestamp,
+                  kMachineProcessFlag flags);
+
+    pid_t pid() const                                                  { return _pid; }
+    const char* name() const                                           { return _name; }
+    AbsInterval timespan() const                                       { return _timespan; }
+    AbsTime exit_timestamp() const                                     { return _exit_initiated_timestamp; }
+    int32_t exit_status() const                                                { return _exit_status; }
+    int32_t apptype() const                                            { return _apptype; }
+
+    uint32_t flags() const                                             { return _flags; }
+
+    const std::vector<const MachineThread<SIZE>*>& threads() const     { return *reinterpret_cast<const std::vector<const MachineThread<SIZE>*>*>(&_threads_by_time); }
+
+    bool is_exit_by_syscall() const                                    { return is_flag_set(kMachineProcessFlag::IsExitBySyscall); }
+    bool is_exit_by_jetsam() const                                     { return is_flag_set(kMachineProcessFlag::IsExitByJetsam); }
+    bool is_exit_by_exec() const                                       { return is_flag_set(kMachineProcessFlag::IsExitByExec); }
+    
+                                                                       // The invariant is that trace_terminated may not be set without is_exiting() set
+    bool is_exiting() const                                            { return is_exit_by_syscall() || is_exit_by_jetsam() || is_exit_by_exec(); }
+    bool is_trace_terminated() const                                   { return is_flag_set(kMachineProcessFlag::IsTraceTerminated); }
+
+    bool is_unknown() const                                            { return is_flag_set(kMachineProcessFlag::IsUnknownProcess); }
+    bool is_kernel() const                                             { return is_flag_set(kMachineProcessFlag::IsKernelProcess); }
+
+    bool is_created_by_previous_machine_state() const                  { return is_flag_set(kMachineProcessFlag::CreatedByPreviousMachineState); }
+    bool is_created_by_thread_map() const                              { return is_flag_set(kMachineProcessFlag::CreatedByThreadMap); }
+    bool is_created_by_fork_exec() const                               { return is_flag_set(kMachineProcessFlag::CreatedByForkExecEvent); }
+    bool is_created_by_exec() const                                    { return is_flag_set(kMachineProcessFlag::CreatedByExecEvent); }
+
+    DEBUG_ONLY(void validate() const;)
+};
+