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 <execinfo.h>
13 #include <sys/types.h>
14 #include <sys/user.h>
15 #include <sys/utsname.h>
16 #include <sys/sysctl.h>
17 
18 // C++ Includes
19 // Other libraries and framework includes
20 // Project includes
21 #include "lldb/Core/Error.h"
22 #include "lldb/Host/Endian.h"
23 #include "lldb/Host/Host.h"
24 #include "lldb/Core/DataExtractor.h"
25 #include "lldb/Core/StreamFile.h"
26 #include "lldb/Core/StreamString.h"
27 #include "lldb/Target/Process.h"
28 
29 #include "llvm/Support/Host.h"
30 
31 extern "C" {
32     char **environ;
33 }
34 
35 using namespace lldb;
36 using namespace lldb_private;
37 
38 class FreeBSDThread
39 {
40 public:
41     FreeBSDThread(const char *thread_name)
42     {
43         Host::SetThreadName (LLDB_INVALID_PROCESS_ID, LLDB_INVALID_THREAD_ID, thread_name);
44     }
45     static void PThreadDestructor (void *v)
46     {
47         delete (FreeBSDThread*)v;
48     }
49 };
50 
51 static pthread_once_t g_thread_create_once = PTHREAD_ONCE_INIT;
52 static pthread_key_t g_thread_create_key = 0;
53 
54 static void
55 InitThreadCreated()
56 {
57     ::pthread_key_create (&g_thread_create_key, FreeBSDThread::PThreadDestructor);
58 }
59 
60 void
61 Host::ThreadCreated (const char *thread_name)
62 {
63     ::pthread_once (&g_thread_create_once, InitThreadCreated);
64     if (g_thread_create_key)
65     {
66         ::pthread_setspecific (g_thread_create_key, new FreeBSDThread(thread_name));
67     }
68 }
69 
70 void
71 Host::Backtrace (Stream &strm, uint32_t max_frames)
72 {
73     char backtrace_path[] = "/tmp/lldb-backtrace-tmp-XXXXXX";
74     int backtrace_fd = ::mkstemp (backtrace_path);
75     if (backtrace_fd != -1)
76     {
77         std::vector<void *> frame_buffer (max_frames, NULL);
78         int count = ::backtrace (&frame_buffer[0], frame_buffer.size());
79         ::backtrace_symbols_fd (&frame_buffer[0], count, backtrace_fd);
80 
81         const off_t buffer_size = ::lseek(backtrace_fd, 0, SEEK_CUR);
82 
83         if (::lseek(backtrace_fd, 0, SEEK_SET) == 0)
84         {
85             char *buffer = (char *)::malloc (buffer_size);
86             if (buffer)
87             {
88                 ssize_t bytes_read = ::read (backtrace_fd, buffer, buffer_size);
89                 if (bytes_read > 0)
90                     strm.Write(buffer, bytes_read);
91                 ::free (buffer);
92             }
93         }
94         ::close (backtrace_fd);
95         ::unlink (backtrace_path);
96     }
97 }
98 
99 size_t
100 Host::GetEnvironment (StringList &env)
101 {
102     char *v;
103     char **var = environ;
104     for (var = environ; var != NULL; ++var) {
105         v = strchr(*var, (int)'-');
106         if (v == NULL)
107             continue;
108         env.AppendString(v);
109     }
110     return env.GetSize();
111 }
112 
113 bool
114 Host::GetOSVersion(uint32_t &major,
115                    uint32_t &minor,
116                    uint32_t &update)
117 {
118     struct utsname un;
119     int status;
120 
121     if (uname(&un) < 0)
122         return false;
123 
124     status = sscanf(un.release, "%u.%u-%u", &major, &minor, &update);
125     return status == 3;
126 }
127 
128 Error
129 Host::LaunchProcess (ProcessLaunchInfo &launch_info)
130 {
131     Error error;
132     assert(!"Not implemented yet!!!");
133     return error;
134 }
135 
136 bool
137 Host::GetOSBuildString (std::string &s)
138 {
139     int mib[2] = { CTL_KERN, KERN_OSREV };
140     char cstr[PATH_MAX];
141     size_t cstr_len = sizeof(cstr);
142     if (::sysctl (mib, 2, cstr, &cstr_len, NULL, 0) == 0)
143     {
144         s.assign (cstr, cstr_len);
145         return true;
146     }
147     s.clear();
148     return false;
149 }
150 
151 bool
152 Host::GetOSKernelDescription (std::string &s)
153 {
154     int mib[2] = { CTL_KERN, KERN_VERSION };
155     char cstr[PATH_MAX];
156     size_t cstr_len = sizeof(cstr);
157     if (::sysctl (mib, 2, cstr, &cstr_len, NULL, 0) == 0)
158     {
159         s.assign (cstr, cstr_len);
160         return true;
161     }
162     s.clear();
163     return false;
164 }
165 
166 static bool
167 GetFreeBSDProcessArgs (const ProcessInstanceInfoMatch *match_info_ptr,
168                       ProcessInstanceInfo &process_info)
169 {
170     if (process_info.ProcessIDIsValid()) {
171         int mib[3] = { CTL_KERN, KERN_PROC_ARGS, process_info.GetProcessID() };
172 
173         char arg_data[8192];
174         size_t arg_data_size = sizeof(arg_data);
175         if (::sysctl (mib, 3, arg_data, &arg_data_size , NULL, 0) == 0)
176         {
177             DataExtractor data (arg_data, arg_data_size, lldb::endian::InlHostByteOrder(), sizeof(void *));
178             uint32_t offset = 0;
179             uint32_t start_offset;
180             uint32_t argc = data.GetU32 (&offset);
181             const char *cstr;
182 
183             cstr = data.GetCStr (&offset);
184             if (cstr)
185             {
186                 process_info.GetExecutableFile().SetFile(cstr, false);
187 
188                 if (match_info_ptr == NULL ||
189                     NameMatches (process_info.GetExecutableFile().GetFilename().GetCString(),
190                                  match_info_ptr->GetNameMatchType(),
191                                  match_info_ptr->GetProcessInfo().GetName()))
192                 {
193                     // Skip NULLs
194                     while (1)
195                     {
196                         const uint8_t *p = data.PeekData(offset, 1);
197                         if ((p == NULL) || (*p != '\0'))
198                             break;
199                         ++offset;
200                     }
201                     // Now extract all arguments
202                     Args &proc_args = process_info.GetArguments();
203                     for (int i=0; i<argc; ++i)
204                     {
205                         start_offset = offset;
206                         cstr = data.GetCStr(&offset);
207                         if (cstr)
208                             proc_args.AppendArgument(cstr);
209                     }
210                     return true;
211                 }
212             }
213         }
214     }
215     return false;
216 }
217 
218 static bool
219 GetFreeBSDProcessCPUType (ProcessInstanceInfo &process_info)
220 {
221     if (process_info.ProcessIDIsValid()) {
222         // TODO: This
223         // return true;
224     }
225     process_info.GetArchitecture().Clear();
226     return false;
227 }
228 
229 static bool
230 GetFreeBSDProcessUserAndGroup(ProcessInstanceInfo &process_info)
231 {
232     struct kinfo_proc proc_kinfo;
233     size_t proc_kinfo_size;
234 
235     if (process_info.ProcessIDIsValid())
236     {
237         int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PID,
238             process_info.GetProcessID() };
239         proc_kinfo_size = sizeof(struct kinfo_proc);
240 
241         if (::sysctl (mib, 4, &proc_kinfo, &proc_kinfo_size, NULL, 0) == 0)
242         {
243             if (proc_kinfo_size > 0)
244             {
245                 process_info.SetParentProcessID (proc_kinfo.ki_ppid);
246                 process_info.SetUserID (proc_kinfo.ki_ruid);
247                 process_info.SetGroupID (proc_kinfo.ki_rgid);
248                 process_info.SetEffectiveUserID (proc_kinfo.ki_uid);
249                 if (proc_kinfo.ki_ngroups > 0)
250                     process_info.SetEffectiveGroupID (proc_kinfo.ki_groups[0]);
251                 else
252                     process_info.SetEffectiveGroupID (UINT32_MAX);
253                 return true;
254             }
255         }
256     }
257     process_info.SetParentProcessID (LLDB_INVALID_PROCESS_ID);
258     process_info.SetUserID (UINT32_MAX);
259     process_info.SetGroupID (UINT32_MAX);
260     process_info.SetEffectiveUserID (UINT32_MAX);
261     process_info.SetEffectiveGroupID (UINT32_MAX);
262     return false;
263 }
264 
265 bool
266 Host::GetProcessInfo (lldb::pid_t pid, ProcessInstanceInfo &process_info)
267 {
268     process_info.SetProcessID(pid);
269     if (GetFreeBSDProcessArgs(NULL, process_info)) {
270         // should use libprocstat instead of going right into sysctl?
271         GetFreeBSDProcessCPUType(process_info);
272         GetFreeBSDProcessUserAndGroup(process_info);
273         return true;
274     }
275     process_info.Clear();
276     return false;
277 }
278