1//===-- source/Host/freebsd/Host.cpp ------------------------------*- C++ -*-===//
2//
3//                     The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9
10// C Includes
11#include <stdio.h>
12#include <dlfcn.h>
13#include <execinfo.h>
14#include <sys/types.h>
15#include <sys/user.h>
16#include <sys/utsname.h>
17#include <sys/sysctl.h>
18
19#include <sys/ptrace.h>
20#include <sys/exec.h>
21#include <machine/elf.h>
22
23
24// C++ Includes
25// Other libraries and framework includes
26// Project includes
27#include "lldb/Core/Error.h"
28#include "lldb/Host/Endian.h"
29#include "lldb/Host/Host.h"
30#include "lldb/Core/DataExtractor.h"
31#include "lldb/Core/StreamFile.h"
32#include "lldb/Core/StreamString.h"
33#include "lldb/Target/Process.h"
34
35#include "lldb/Core/DataBufferHeap.h"
36#include "lldb/Core/DataExtractor.h"
37#include "llvm/Support/Host.h"
38
39
40extern "C" {
41    extern char **environ;
42}
43
44using namespace lldb;
45using namespace lldb_private;
46
47
48class FreeBSDThread
49{
50public:
51    FreeBSDThread(const char *thread_name)
52    {
53        Host::SetThreadName (LLDB_INVALID_PROCESS_ID, LLDB_INVALID_THREAD_ID, thread_name);
54    }
55    static void PThreadDestructor (void *v)
56    {
57        delete (FreeBSDThread*)v;
58    }
59};
60
61static pthread_once_t g_thread_create_once = PTHREAD_ONCE_INIT;
62static pthread_key_t g_thread_create_key = 0;
63
64static void
65InitThreadCreated()
66{
67    ::pthread_key_create (&g_thread_create_key, FreeBSDThread::PThreadDestructor);
68}
69
70void
71Host::ThreadCreated (const char *thread_name)
72{
73    ::pthread_once (&g_thread_create_once, InitThreadCreated);
74    if (g_thread_create_key)
75    {
76        ::pthread_setspecific (g_thread_create_key, new FreeBSDThread(thread_name));
77    }
78
79    Host::SetShortThreadName (LLDB_INVALID_PROCESS_ID, LLDB_INVALID_THREAD_ID, thread_name, 16);
80}
81
82std::string
83Host::GetThreadName (lldb::pid_t pid, lldb::tid_t tid)
84{
85    std::string thread_name;
86    return thread_name;
87}
88
89void
90Host::Backtrace (Stream &strm, uint32_t max_frames)
91{
92    char backtrace_path[] = "/tmp/lldb-backtrace-tmp-XXXXXX";
93    int backtrace_fd = ::mkstemp (backtrace_path);
94    if (backtrace_fd != -1)
95    {
96        std::vector<void *> frame_buffer (max_frames, NULL);
97        int count = ::backtrace (&frame_buffer[0], frame_buffer.size());
98        ::backtrace_symbols_fd (&frame_buffer[0], count, backtrace_fd);
99
100        const off_t buffer_size = ::lseek(backtrace_fd, 0, SEEK_CUR);
101
102        if (::lseek(backtrace_fd, 0, SEEK_SET) == 0)
103        {
104            char *buffer = (char *)::malloc (buffer_size);
105            if (buffer)
106            {
107                ssize_t bytes_read = ::read (backtrace_fd, buffer, buffer_size);
108                if (bytes_read > 0)
109                    strm.Write(buffer, bytes_read);
110                ::free (buffer);
111            }
112        }
113        ::close (backtrace_fd);
114        ::unlink (backtrace_path);
115    }
116}
117
118size_t
119Host::GetEnvironment (StringList &env)
120{
121    char *v;
122    char **var = environ;
123    for (; var != NULL && *var != NULL; ++var) {
124        v = strchr(*var, (int)'-');
125        if (v == NULL)
126            continue;
127        env.AppendString(v);
128    }
129    return env.GetSize();
130}
131
132bool
133Host::GetOSVersion(uint32_t &major,
134                   uint32_t &minor,
135                   uint32_t &update)
136{
137    struct utsname un;
138    int status;
139
140    if (uname(&un) < 0)
141        return false;
142
143    status = sscanf(un.release, "%u.%u", &major, &minor);
144    return status == 2;
145}
146
147Error
148Host::LaunchProcess (ProcessLaunchInfo &launch_info)
149{
150    Error error;
151    assert(!"Not implemented yet!!!");
152    return error;
153}
154
155bool
156Host::GetOSBuildString (std::string &s)
157{
158    int mib[2] = { CTL_KERN, KERN_OSREV };
159    char cstr[PATH_MAX];
160    size_t cstr_len = sizeof(cstr);
161    if (::sysctl (mib, 2, cstr, &cstr_len, NULL, 0) == 0)
162    {
163        s.assign (cstr, cstr_len);
164        return true;
165    }
166    s.clear();
167    return false;
168}
169
170bool
171Host::GetOSKernelDescription (std::string &s)
172{
173    int mib[2] = { CTL_KERN, KERN_VERSION };
174    char cstr[PATH_MAX];
175    size_t cstr_len = sizeof(cstr);
176    if (::sysctl (mib, 2, cstr, &cstr_len, NULL, 0) == 0)
177    {
178        s.assign (cstr, cstr_len);
179        return true;
180    }
181    s.clear();
182    return false;
183}
184
185static bool
186GetFreeBSDProcessArgs (const ProcessInstanceInfoMatch *match_info_ptr,
187                      ProcessInstanceInfo &process_info)
188{
189    if (process_info.ProcessIDIsValid()) {
190        int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_ARGS, (int)process_info.GetProcessID() };
191
192        char arg_data[8192];
193        size_t arg_data_size = sizeof(arg_data);
194        if (::sysctl (mib, 4, arg_data, &arg_data_size , NULL, 0) == 0)
195        {
196            DataExtractor data (arg_data, arg_data_size, lldb::endian::InlHostByteOrder(), sizeof(void *));
197            lldb::offset_t offset = 0;
198            const char *cstr;
199
200            cstr = data.GetCStr (&offset);
201            if (cstr)
202            {
203                process_info.GetExecutableFile().SetFile(cstr, false);
204
205                if (!(match_info_ptr == NULL ||
206                    NameMatches (process_info.GetExecutableFile().GetFilename().GetCString(),
207                                 match_info_ptr->GetNameMatchType(),
208                                 match_info_ptr->GetProcessInfo().GetName())))
209                    return false;
210
211                Args &proc_args = process_info.GetArguments();
212                while (1)
213                {
214                    const uint8_t *p = data.PeekData(offset, 1);
215                    while ((p != NULL) && (*p == '\0') && offset < arg_data_size)
216                    {
217                        ++offset;
218                        p = data.PeekData(offset, 1);
219                    }
220                    if (p == NULL || offset >= arg_data_size)
221                        return true;
222
223                    cstr = data.GetCStr(&offset);
224                    if (cstr)
225                        proc_args.AppendArgument(cstr);
226                    else
227                        return true;
228                }
229            }
230        }
231    }
232    return false;
233}
234
235static bool
236GetFreeBSDProcessCPUType (ProcessInstanceInfo &process_info)
237{
238    if (process_info.ProcessIDIsValid()) {
239        process_info.GetArchitecture() = Host::GetArchitecture (Host::eSystemDefaultArchitecture);
240        return true;
241    }
242    process_info.GetArchitecture().Clear();
243    return false;
244}
245
246static bool
247GetFreeBSDProcessUserAndGroup(ProcessInstanceInfo &process_info)
248{
249    struct kinfo_proc proc_kinfo;
250    size_t proc_kinfo_size;
251
252    if (process_info.ProcessIDIsValid())
253    {
254        int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PID,
255            (int)process_info.GetProcessID() };
256        proc_kinfo_size = sizeof(struct kinfo_proc);
257
258        if (::sysctl (mib, 4, &proc_kinfo, &proc_kinfo_size, NULL, 0) == 0)
259        {
260            if (proc_kinfo_size > 0)
261            {
262                process_info.SetParentProcessID (proc_kinfo.ki_ppid);
263                process_info.SetUserID (proc_kinfo.ki_ruid);
264                process_info.SetGroupID (proc_kinfo.ki_rgid);
265                process_info.SetEffectiveUserID (proc_kinfo.ki_uid);
266                if (proc_kinfo.ki_ngroups > 0)
267                    process_info.SetEffectiveGroupID (proc_kinfo.ki_groups[0]);
268                else
269                    process_info.SetEffectiveGroupID (UINT32_MAX);
270                return true;
271            }
272        }
273    }
274    process_info.SetParentProcessID (LLDB_INVALID_PROCESS_ID);
275    process_info.SetUserID (UINT32_MAX);
276    process_info.SetGroupID (UINT32_MAX);
277    process_info.SetEffectiveUserID (UINT32_MAX);
278    process_info.SetEffectiveGroupID (UINT32_MAX);
279    return false;
280}
281
282bool
283Host::GetProcessInfo (lldb::pid_t pid, ProcessInstanceInfo &process_info)
284{
285    process_info.SetProcessID(pid);
286    if (GetFreeBSDProcessArgs(NULL, process_info)) {
287        // should use libprocstat instead of going right into sysctl?
288        GetFreeBSDProcessCPUType(process_info);
289        GetFreeBSDProcessUserAndGroup(process_info);
290        return true;
291    }
292    process_info.Clear();
293    return false;
294}
295
296lldb::DataBufferSP
297Host::GetAuxvData(lldb_private::Process *process)
298{
299   int mib[2] = { CTL_KERN, KERN_PS_STRINGS };
300   void *ps_strings_addr, *auxv_addr;
301   size_t ps_strings_size = sizeof(void *);
302   Elf_Auxinfo aux_info[AT_COUNT];
303   struct ps_strings ps_strings;
304   struct ptrace_io_desc pid;
305   DataBufferSP buf_sp;
306   std::unique_ptr<DataBufferHeap> buf_ap(new DataBufferHeap(1024, 0));
307
308   if (::sysctl(mib, 2, &ps_strings_addr, &ps_strings_size, NULL, 0) == 0) {
309           pid.piod_op = PIOD_READ_D;
310           pid.piod_addr = &ps_strings;
311           pid.piod_offs = ps_strings_addr;
312           pid.piod_len = sizeof(ps_strings);
313           if (::ptrace(PT_IO, process->GetID(), (caddr_t)&pid, 0)) {
314                   perror("failed to fetch ps_strings");
315                   buf_ap.release();
316                   goto done;
317           }
318
319           auxv_addr = ps_strings.ps_envstr + ps_strings.ps_nenvstr + 1;
320
321           pid.piod_addr = aux_info;
322           pid.piod_offs = auxv_addr;
323           pid.piod_len = sizeof(aux_info);
324           if (::ptrace(PT_IO, process->GetID(), (caddr_t)&pid, 0)) {
325                   perror("failed to fetch aux_info");
326                   buf_ap.release();
327                   goto done;
328           }
329           memcpy(buf_ap->GetBytes(), aux_info, pid.piod_len);
330           buf_sp.reset(buf_ap.release());
331   } else {
332           perror("sysctl failed on ps_strings");
333   }
334
335   done:
336   return buf_sp;
337}
338