blob: 1fd2d526d25018db19725f8092a7388032c678ee [file] [log] [blame]
Chris Lattner30fdc8d2010-06-08 16:52:24 +00001//===-- ProfileObjectiveC.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// Created by Greg Clayton on 10/4/07.
11//
12//===----------------------------------------------------------------------===//
13
14#include "ProfileObjectiveC.h"
15#include "DNB.h"
16#include <objc/objc-runtime.h>
17#include <map>
18
19#if defined (__powerpc__) || defined (__ppc__)
20#define OBJC_MSG_SEND_PPC32_COMM_PAGE_ADDR ((nub_addr_t)0xfffeff00)
21#endif
22
23//----------------------------------------------------------------------
24// Constructor
25//----------------------------------------------------------------------
26ProfileObjectiveC::ProfileObjectiveC() :
27 m_pid(INVALID_NUB_PROCESS),
28 m_objcStats(),
29 m_hit_count(0),
30 m_dump_count(0xffff)
31{
32 memset(&m_begin_time, 0, sizeof(m_begin_time));
33}
34
35//----------------------------------------------------------------------
36// Destructor
37//----------------------------------------------------------------------
38ProfileObjectiveC::~ProfileObjectiveC()
39{
40}
41
42//----------------------------------------------------------------------
43// Clear any counts that we may have had
44//----------------------------------------------------------------------
45void
46ProfileObjectiveC::Clear()
47{
48 if (m_pid != INVALID_NUB_PROCESS)
49 {
50 DNBBreakpointClear(m_pid, m_objc_msgSend.breakID);
51 DNBBreakpointClear(m_pid, m_objc_msgSendSuper.breakID);
52#if defined (__powerpc__) || defined (__ppc__)
53 DNBBreakpointClear(m_pid, m_objc_msgSend_rtp.breakID);
54#endif
55 }
56 m_objc_msgSend.Clear();
57 m_objc_msgSendSuper.Clear();
58#if defined (__powerpc__) || defined (__ppc__)
59 memset(m_objc_msgSend_opcode, 0, k_opcode_size);
60 m_objc_msgSend_rtp.Clear();
61#endif
62 memset(&m_begin_time, 0, sizeof(m_begin_time));
63 m_objcStats.clear();
64}
65
66void
67ProfileObjectiveC::Initialize(nub_process_t pid)
68{
69 Clear();
70 m_pid = pid;
71}
72
73
74void
75ProfileObjectiveC::ProcessStateChanged(nub_state_t state)
76{
77 //printf("ProfileObjectiveC::%s(%s)\n", __FUNCTION__, DNBStateAsString(state));
78 switch (state)
79 {
80 case eStateInvalid:
81 case eStateUnloaded:
82 case eStateExited:
83 case eStateDetached:
84 Clear();
85 break;
86
87 case eStateStopped:
88#if defined (__powerpc__) || defined (__ppc__)
89 if (NUB_BREAK_ID_IS_VALID(m_objc_msgSend.breakID) && !NUB_BREAK_ID_IS_VALID(m_objc_msgSend_rtp.breakID))
90 {
91 nub_thread_t tid = DNBProcessGetCurrentThread(m_pid);
92 DNBRegisterValue pc_value;
93 if (DNBThreadGetRegisterValueByName(m_pid, tid, REGISTER_SET_ALL, "srr0" , &pc_value))
94 {
95 nub_addr_t pc = pc_value.value.uint32;
96 if (pc == OBJC_MSG_SEND_PPC32_COMM_PAGE_ADDR)
97 {
98 // Restore previous first instruction to 0xfffeff00 in comm page
99 DNBProcessMemoryWrite(m_pid, OBJC_MSG_SEND_PPC32_COMM_PAGE_ADDR, k_opcode_size, m_objc_msgSend_opcode);
100 //printf("Setting breakpoint on _objc_msgSend_rtp...\n");
101 m_objc_msgSend_rtp.breakID = DNBBreakpointSet(m_pid, OBJC_MSG_SEND_PPC32_COMM_PAGE_ADDR);
102 if (NUB_BREAK_ID_IS_VALID(m_objc_msgSend_rtp.breakID))
103 {
104 DNBBreakpointSetCallback(m_pid, m_objc_msgSend_rtp.breakID, ProfileObjectiveC::MessageSendBreakpointCallback, this);
105 }
106 }
107 }
108 }
109#endif
110 DumpStats(m_pid, stdout);
111 break;
112
113 case eStateAttaching:
114 case eStateLaunching:
115 case eStateRunning:
116 case eStateStepping:
117 case eStateCrashed:
118 case eStateSuspended:
119 break;
120
121 default:
122 break;
123 }
124}
125
126void
127ProfileObjectiveC::SharedLibraryStateChanged(DNBExecutableImageInfo *image_infos, nub_size_t num_image_infos)
128{
129 //printf("ProfileObjectiveC::%s(%p, %u)\n", __FUNCTION__, image_infos, num_image_infos);
130 if (m_objc_msgSend.IsValid() && m_objc_msgSendSuper.IsValid())
131 return;
132
133 if (image_infos)
134 {
135 nub_process_t pid = m_pid;
136 nub_size_t i;
137 for (i = 0; i < num_image_infos; i++)
138 {
139 if (strcmp(image_infos[i].name, "/usr/lib/libobjc.A.dylib") == 0)
140 {
141 if (!NUB_BREAK_ID_IS_VALID(m_objc_msgSend.breakID))
142 {
143 m_objc_msgSend.addr = DNBProcessLookupAddress(pid, "_objc_msgSend", image_infos[i].name);
144
145 if (m_objc_msgSend.addr != INVALID_NUB_ADDRESS)
146 {
147#if defined (__powerpc__) || defined (__ppc__)
148 if (DNBProcessMemoryRead(pid, m_objc_msgSend.addr, k_opcode_size, m_objc_msgSend_opcode) != k_opcode_size)
149 memset(m_objc_msgSend_opcode, 0, sizeof(m_objc_msgSend_opcode));
150#endif
151 m_objc_msgSend.breakID = DNBBreakpointSet(pid, m_objc_msgSend.addr, 4, false);
152 if (NUB_BREAK_ID_IS_VALID(m_objc_msgSend.breakID))
153 DNBBreakpointSetCallback(pid, m_objc_msgSend.breakID, ProfileObjectiveC::MessageSendBreakpointCallback, this);
154 }
155 }
156
157 if (!NUB_BREAK_ID_IS_VALID(m_objc_msgSendSuper.breakID))
158 {
159 m_objc_msgSendSuper.addr = DNBProcessLookupAddress(pid, "_objc_msgSendSuper", image_infos[i].name);
160
161 if (m_objc_msgSendSuper.addr != INVALID_NUB_ADDRESS)
162 {
163 m_objc_msgSendSuper.breakID = DNBBreakpointSet(pid, m_objc_msgSendSuper.addr, 4, false);
164 if (NUB_BREAK_ID_IS_VALID(m_objc_msgSendSuper.breakID))
165 DNBBreakpointSetCallback(pid, m_objc_msgSendSuper.breakID, ProfileObjectiveC::MessageSendSuperBreakpointCallback, this);
166 }
167 }
168 break;
169 }
170 }
171 }
172}
173
174
175void
176ProfileObjectiveC::SetStartTime()
177{
178 gettimeofday(&m_begin_time, NULL);
179}
180
181void
182ProfileObjectiveC::SelectorHit(objc_class_ptr_t isa, objc_selector_t sel)
183{
184 m_objcStats[isa][sel]++;
185}
186
187nub_bool_t
188ProfileObjectiveC::MessageSendBreakpointCallback(nub_process_t pid, nub_thread_t tid, nub_break_t breakID, void *userData)
189{
190 ProfileObjectiveC *profile_objc = (ProfileObjectiveC*)userData;
191 uint32_t hit_count = profile_objc->IncrementHitCount();
192 if (hit_count == 1)
193 profile_objc->SetStartTime();
194
195 objc_class_ptr_t objc_self = 0;
196 objc_selector_t objc_selector = 0;
197#if defined (__i386__)
198 DNBRegisterValue esp;
199 if (DNBThreadGetRegisterValueByName(pid, tid, REGISTER_SET_ALL, "esp", &esp))
200 {
201 uint32_t uval32[2];
202 if (DNBProcessMemoryRead(pid, esp.value.uint32 + 4, 8, &uval32) == 8)
203 {
204 objc_self = uval32[0];
205 objc_selector = uval32[1];
206 }
207 }
208#elif defined (__powerpc__) || defined (__ppc__)
209 DNBRegisterValue r3;
210 DNBRegisterValue r4;
211 if (DNBThreadGetRegisterValueByName(pid, tid, REGISTER_SET_ALL, "r3", &r3) &&
212 DNBThreadGetRegisterValueByName(pid, tid, REGISTER_SET_ALL, "r4", &r4))
213 {
214 objc_self = r3.value.uint32;
215 objc_selector = r4.value.uint32;
216 }
217#elif defined (__arm__)
218 DNBRegisterValue r0;
219 DNBRegisterValue r1;
220 if (DNBThreadGetRegisterValueByName(pid, tid, REGISTER_SET_ALL, "r0", &r0) &&
221 DNBThreadGetRegisterValueByName(pid, tid, REGISTER_SET_ALL, "r1", &r1))
222 {
223 objc_self = r0.value.uint32;
224 objc_selector = r1.value.uint32;
225 }
226#else
227#error undefined architecture
228#endif
229 if (objc_selector != 0)
230 {
231 uint32_t isa = 0;
232 if (objc_self == 0)
233 {
234 profile_objc->SelectorHit(0, objc_selector);
235 }
236 else
237 if (DNBProcessMemoryRead(pid, (nub_addr_t)objc_self, sizeof(isa), &isa) == sizeof(isa))
238 {
239 if (isa)
240 {
241 profile_objc->SelectorHit(isa, objc_selector);
242 }
243 else
244 {
245 profile_objc->SelectorHit(0, objc_selector);
246 }
247 }
248 }
249
250
251 // Dump stats if we are supposed to
252 if (profile_objc->ShouldDumpStats())
253 {
254 profile_objc->DumpStats(pid, stdout);
255 return true;
256 }
257
258 // Just let the target run again by returning false;
259 return false;
260}
261
262nub_bool_t
263ProfileObjectiveC::MessageSendSuperBreakpointCallback(nub_process_t pid, nub_thread_t tid, nub_break_t breakID, void *userData)
264{
265 ProfileObjectiveC *profile_objc = (ProfileObjectiveC*)userData;
266
267 uint32_t hit_count = profile_objc->IncrementHitCount();
268 if (hit_count == 1)
269 profile_objc->SetStartTime();
270
271// printf("BreakID %u hit count is = %u\n", breakID, hc);
272 objc_class_ptr_t objc_super = 0;
273 objc_selector_t objc_selector = 0;
274#if defined (__i386__)
275 DNBRegisterValue esp;
276 if (DNBThreadGetRegisterValueByName(pid, tid, REGISTER_SET_ALL, "esp", &esp))
277 {
278 uint32_t uval32[2];
279 if (DNBProcessMemoryRead(pid, esp.value.uint32 + 4, 8, &uval32) == 8)
280 {
281 objc_super = uval32[0];
282 objc_selector = uval32[1];
283 }
284 }
285#elif defined (__powerpc__) || defined (__ppc__)
286 DNBRegisterValue r3;
287 DNBRegisterValue r4;
288 if (DNBThreadGetRegisterValueByName(pid, tid, REGISTER_SET_ALL, "r3", &r3) &&
289 DNBThreadGetRegisterValueByName(pid, tid, REGISTER_SET_ALL, "r4", &r4))
290 {
291 objc_super = r3.value.uint32;
292 objc_selector = r4.value.uint32;
293 }
294#elif defined (__arm__)
295 DNBRegisterValue r0;
296 DNBRegisterValue r1;
297 if (DNBThreadGetRegisterValueByName(pid, tid, REGISTER_SET_ALL, "r0", &r0) &&
298 DNBThreadGetRegisterValueByName(pid, tid, REGISTER_SET_ALL, "r1", &r1))
299 {
300 objc_super = r0.value.uint32;
301 objc_selector = r1.value.uint32;
302 }
303#else
304#error undefined architecture
305#endif
306 if (objc_selector != 0)
307 {
308 uint32_t isa = 0;
309 if (objc_super == 0)
310 {
311 profile_objc->SelectorHit(0, objc_selector);
312 }
313 else
314 if (DNBProcessMemoryRead(pid, (nub_addr_t)objc_super + 4, sizeof(isa), &isa) == sizeof(isa))
315 {
316 if (isa)
317 {
318 profile_objc->SelectorHit(isa, objc_selector);
319 }
320 else
321 {
322 profile_objc->SelectorHit(0, objc_selector);
323 }
324 }
325 }
326
327 // Dump stats if we are supposed to
328 if (profile_objc->ShouldDumpStats())
329 {
330 profile_objc->DumpStats(pid, stdout);
331 return true;
332 }
333
334 // Just let the target run again by returning false;
335 return false;
336}
337
338void
339ProfileObjectiveC::DumpStats(nub_process_t pid, FILE *f)
340{
341 if (f == NULL)
342 return;
343
344 if (m_hit_count == 0)
345 return;
346
347 ClassStatsMap::iterator class_pos;
348 ClassStatsMap::iterator class_end = m_objcStats.end();
349
350 struct timeval end_time;
351 gettimeofday(&end_time, NULL);
352 int64_t elapsed_usec = ((int64_t)(1000*1000))*((int64_t)end_time.tv_sec - (int64_t)m_begin_time.tv_sec) + ((int64_t)end_time.tv_usec - (int64_t)m_begin_time.tv_usec);
353 fprintf(f, "%u probe hits for %.2f hits/sec)\n", m_hit_count, (double)m_hit_count / (((double)elapsed_usec)/(1000000.0)));
354
355 for (class_pos = m_objcStats.begin(); class_pos != class_end; ++class_pos)
356 {
357 SelectorHitCount::iterator sel_pos;
358 SelectorHitCount::iterator sel_end = class_pos->second.end();
359 for (sel_pos = class_pos->second.begin(); sel_pos != sel_end; ++sel_pos)
360 {
361 struct objc_class objc_class;
362 uint32_t isa = class_pos->first;
363 uint32_t sel = sel_pos->first;
364 uint32_t sel_hit_count = sel_pos->second;
365
366 if (isa != 0 && DNBProcessMemoryRead(pid, isa, sizeof(objc_class), &objc_class) == sizeof(objc_class))
367 {
368 /* fprintf(f, "%#.8x\n isa = %p\n super_class = %p\n name = %p\n version = %lx\n info = %lx\ninstance_size = %lx\n ivars = %p\n methodLists = %p\n cache = %p\n protocols = %p\n",
369 arg1.value.pointer,
370 objc_class.isa,
371 objc_class.super_class,
372 objc_class.name,
373 objc_class.version,
374 objc_class.info,
375 objc_class.instance_size,
376 objc_class.ivars,
377 objc_class.methodLists,
378 objc_class.cache,
379 objc_class.protocols); */
380
381 // Print the class name
382 fprintf(f, "%6u hits for %c[", sel_hit_count, (objc_class.super_class == objc_class.isa ? '+' : '-'));
383 DNBPrintf(pid, INVALID_NUB_THREAD, (nub_addr_t)objc_class.name, f, "%s ");
384 }
385 else
386 {
387 fprintf(f, "%6u hits for [<nil> ", sel_hit_count);
388 }
389 DNBPrintf(pid, INVALID_NUB_THREAD, sel, f, "%s]\n");
390 }
391 }
392}
393