profile.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414
  1. /*
  2. * Mach Operating System
  3. * Copyright (c) 1991,1990,1989 Carnegie Mellon University.
  4. * Copyright (c) 1993,1994 The University of Utah and
  5. * the Computer Systems Laboratory (CSL).
  6. * All rights reserved.
  7. *
  8. * Permission to use, copy, modify and distribute this software and its
  9. * documentation is hereby granted, provided that both the copyright
  10. * notice and this permission notice appear in all copies of the
  11. * software, derivative works or modified versions, and any portions
  12. * thereof, and that both notices appear in supporting documentation.
  13. *
  14. * CARNEGIE MELLON, THE UNIVERSITY OF UTAH AND CSL ALLOW FREE USE OF
  15. * THIS SOFTWARE IN ITS "AS IS" CONDITION, AND DISCLAIM ANY LIABILITY
  16. * OF ANY KIND FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF
  17. * THIS SOFTWARE.
  18. *
  19. * Carnegie Mellon requests users of this software to return to
  20. *
  21. * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
  22. * School of Computer Science
  23. * Carnegie Mellon University
  24. * Pittsburgh PA 15213-3890
  25. *
  26. * any improvements or extensions that they make and grant Carnegie Mellon
  27. * the rights to redistribute these changes.
  28. */
  29. /*
  30. * Copyright 1991 by Open Software Foundation,
  31. * Grenoble, FRANCE
  32. *
  33. * All Rights Reserved
  34. *
  35. * Permission to use, copy, modify, and distribute this software and
  36. * its documentation for any purpose and without fee is hereby granted,
  37. * provided that the above copyright notice appears in all copies and
  38. * that both the copyright notice and this permission notice appear in
  39. * supporting documentation, and that the name of OSF or Open Software
  40. * Foundation not be used in advertising or publicity pertaining to
  41. * distribution of the software without specific, written prior
  42. * permission.
  43. *
  44. * OSF DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE
  45. * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS,
  46. * IN NO EVENT SHALL OSF BE LIABLE FOR ANY SPECIAL, INDIRECT, OR
  47. * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  48. * LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT,
  49. * NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
  50. * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  51. */
  52. #if 0
  53. #include <kern/thread.h>
  54. #include <kern/queue.h>
  55. #include <mach/profil.h>
  56. #include <kern/sched_prim.h>
  57. #include <ipc/ipc_space.h>
  58. extern vm_map_t kernel_map; /* can be discarded, defined in <vm/vm_kern.h> */
  59. thread_t profile_thread_id = THREAD_NULL;
  60. void profile_thread()
  61. {
  62. struct message {
  63. mach_msg_header_t head;
  64. mach_msg_type_t type;
  65. int arg[SIZE_PROF_BUFFER+1];
  66. } msg;
  67. spl_t s;
  68. buf_to_send_t buf_entry;
  69. queue_entry_t prof_queue_entry;
  70. prof_data_t pbuf;
  71. simple_lock_t lock;
  72. msg_return_t mr;
  73. int j;
  74. /* Initialise the queue header for the prof_queue */
  75. mpqueue_init(&prof_queue);
  76. /* Template initialisation of header and type structures */
  77. msg.head.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, MACH_MSG_TYPE_MAKE_SEND_ONCE);
  78. msg.head.msgh_size = sizeof(msg);
  79. msg.head.msgh_local_port = MACH_PORT_NULL;
  80. msg.head.msgh_kind = MACH_MSGH_KIND_NORMAL;
  81. msg.head.msgh_id = 666666;
  82. msg.type.msgt_name = MACH_MSG_TYPE_INTEGER_32;
  83. msg.type.msgt_size = 32;
  84. msg.type.msgt_number = SIZE_PROF_BUFFER+1;
  85. msg.type.msgt_inline = TRUE;
  86. msg.type.msgt_longform = FALSE;
  87. msg.type.msgt_deallocate = FALSE;
  88. msg.type.msgt_unused = 0;
  89. while (TRUE) {
  90. /* Dequeue the first buffer. */
  91. s = splsched();
  92. mpdequeue_head(&prof_queue, &prof_queue_entry);
  93. splx(s);
  94. if ((buf_entry = (buf_to_send_t) prof_queue_entry) == NULLBTS)
  95. {
  96. thread_sleep((event_t) profile_thread, lock, TRUE);
  97. if (current_thread()->wait_result != THREAD_AWAKENED)
  98. break;
  99. }
  100. else {
  101. task_t curr_task;
  102. thread_t curr_th;
  103. int *sample;
  104. int curr_buf;
  105. int imax;
  106. curr_th = (thread_t) buf_entry->thread;
  107. curr_buf = (int) buf_entry->number;
  108. pbuf = curr_th->profil_buffer;
  109. /* Set the remote port */
  110. msg.head.msgh_remote_port = (mach_port_t) pbuf->prof_port;
  111. sample = pbuf->prof_area[curr_buf].p_zone;
  112. imax = pbuf->prof_area[curr_buf].p_index;
  113. for(j=0 ;j<imax; j++,sample++)
  114. msg.arg[j] = *sample;
  115. /* Let hardclock() know you've finished the dirty job */
  116. pbuf->prof_area[curr_buf].p_full = FALSE;
  117. /*
  118. * Store the number of samples actually sent
  119. * as the last element of the array.
  120. */
  121. msg.arg[SIZE_PROF_BUFFER] = imax;
  122. mr = mach_msg(&(msg.head), MACH_SEND_MSG,
  123. sizeof(struct message), 0,
  124. MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE,
  125. MACH_PORT_NULL);
  126. if (mr != MACH_MSG_SUCCESS) {
  127. printf("profile_thread: mach_msg failed returned %x\n",(int)mr);
  128. }
  129. if (buf_entry->wakeme)
  130. thread_wakeup((event_t) &buf_entry->wakeme);
  131. kmem_free(kernel_map, (buf_to_send_t) buf_entry,
  132. sizeof(struct buf_to_send));
  133. }
  134. }
  135. /* The profile thread has been signalled to exit. There may still
  136. be sample data queued for us, which we must now throw away.
  137. Once we set profile_thread_id to null, hardclock() will stop
  138. queueing any additional samples, so we do not need to alter
  139. the interrupt level. */
  140. profile_thread_id = THREAD_NULL;
  141. while (1) {
  142. mpdequeue_head(&prof_queue, &prof_queue_entry);
  143. if ((buf_entry = (buf_to_send_t) prof_queue_entry) == NULLBTS)
  144. break;
  145. if (buf_entry->wakeme)
  146. thread_wakeup((event_t) &buf_entry->wakeme);
  147. kmem_free(kernel_map, (buf_to_send_t) buf_entry,
  148. sizeof(struct buf_to_send));
  149. }
  150. thread_halt_self(thread_exception_return);
  151. }
  152. #include <mach/message.h>
  153. void
  154. send_last_sample_buf(th)
  155. thread_t th;
  156. {
  157. spl_t s;
  158. buf_to_send_t buf_entry;
  159. vm_offset_t vm_buf_entry;
  160. if (th->profil_buffer == NULLPBUF)
  161. return;
  162. /* Ask for the sending of the last PC buffer.
  163. * Make a request to the profile_thread by inserting
  164. * the buffer in the send queue, and wake it up.
  165. * The last buffer must be inserted at the head of the
  166. * send queue, so the profile_thread handles it immediately.
  167. */
  168. if (kmem_alloc( kernel_map, &vm_buf_entry,
  169. sizeof(struct buf_to_send)) != KERN_SUCCESS)
  170. return;
  171. buf_entry = (buf_to_send_t) vm_buf_entry;
  172. buf_entry->thread = (int *) th;
  173. buf_entry->number = th->profil_buffer->prof_index;
  174. /* Watch out in case profile thread exits while we are about to
  175. queue data for it. */
  176. s = splsched();
  177. if (profile_thread_id != THREAD_NULL) {
  178. simple_lock_t lock;
  179. buf_entry->wakeme = 1;
  180. mpenqueue_tail( &prof_queue, &(buf_entry->list));
  181. thread_wakeup((event_t) profile_thread);
  182. assert_wait((event_t) &buf_entry->wakeme, TRUE);
  183. splx(s);
  184. thread_block(thread_no_continuation);
  185. } else {
  186. splx(s);
  187. kmem_free(kernel_map, vm_buf_entry, sizeof(struct buf_to_send));
  188. }
  189. }
  190. /*
  191. * Profile current thread
  192. */
  193. profile(pc) {
  194. /* Find out which thread has been interrupted. */
  195. thread_t it_thread = current_thread();
  196. int inout_val = pc;
  197. buf_to_send_t buf_entry;
  198. vm_offset_t vm_buf_entry;
  199. int *val;
  200. /*
  201. * Test if the current thread is to be sampled
  202. */
  203. if (it_thread->thread_profiled) {
  204. /* Inserts the PC value in the buffer of the thread */
  205. set_pbuf_value(it_thread->profil_buffer, &inout_val);
  206. switch(inout_val) {
  207. case 0:
  208. if (profile_thread_id == THREAD_NULL) {
  209. reset_pbuf_area(it_thread->profil_buffer);
  210. } else printf("ERROR : hardclock : full buffer unsent\n");
  211. break;
  212. case 1:
  213. /* Normal case, value successfully inserted */
  214. break;
  215. case 2 :
  216. /*
  217. * The value we have just inserted caused the
  218. * buffer to be full, and ready to be sent.
  219. * If profile_thread_id is null, the profile
  220. * thread has been killed. Since this generally
  221. * happens only when the O/S server task of which
  222. * it is a part is killed, it is not a great loss
  223. * to throw away the data.
  224. */
  225. if (profile_thread_id == THREAD_NULL ||
  226. kmem_alloc(kernel_map,
  227. &vm_buf_entry ,
  228. sizeof(struct buf_to_send)) !=
  229. KERN_SUCCESS) {
  230. reset_pbuf_area(it_thread->profil_buffer);
  231. break;
  232. }
  233. buf_entry = (buf_to_send_t) vm_buf_entry;
  234. buf_entry->thread = (int *)it_thread;
  235. buf_entry->number =
  236. (it_thread->profil_buffer)->prof_index;
  237. mpenqueue_tail(&prof_queue, &(buf_entry->list));
  238. /* Switch to another buffer */
  239. reset_pbuf_area(it_thread->profil_buffer);
  240. /* Wake up the profile thread */
  241. if (profile_thread_id != THREAD_NULL)
  242. thread_wakeup((event_t) profile_thread);
  243. break;
  244. default:
  245. printf("ERROR: profile : unexpected case\n");
  246. }
  247. }
  248. }
  249. /* The task parameter in this and the subsequent routine is needed for
  250. MiG, even though it is not used in the function itself. */
  251. kern_return_t
  252. mach_sample_thread (task, reply, cur_thread)
  253. ipc_space_t task;
  254. ipc_object_t reply;
  255. thread_t cur_thread;
  256. {
  257. /*
  258. * This routine is called every time that a new thread has made
  259. * a request for the sampling service. We must keep track of the
  260. * correspondance between it's identity (cur_thread) and the port
  261. * we are going to use as a reply port to send out the samples resulting
  262. * from its execution.
  263. */
  264. prof_data_t pbuf;
  265. vm_offset_t vmpbuf;
  266. if (reply != MACH_PORT_NULL) {
  267. if (cur_thread->thread_profiled && cur_thread->thread_profiled_own) {
  268. if (reply == cur_thread->profil_buffer->prof_port)
  269. return KERN_SUCCESS;
  270. mach_sample_thread(MACH_PORT_NULL, cur_thread);
  271. }
  272. /* Start profiling this thread , do the initialization. */
  273. alloc_pbuf_area(pbuf, vmpbuf);
  274. if ((cur_thread->profil_buffer = pbuf) == NULLPBUF) {
  275. printf("ERROR:mach_sample_thread:cannot allocate pbuf\n");
  276. return KERN_RESOURCE_SHORTAGE;
  277. } else {
  278. if (!set_pbuf_nb(pbuf, NB_PROF_BUFFER-1)) {
  279. printf("ERROR:mach_sample_thread:cannot set pbuf_nb\n");
  280. return KERN_FAILURE;
  281. }
  282. reset_pbuf_area(pbuf);
  283. }
  284. pbuf->prof_port = reply;
  285. cur_thread->thread_profiled = TRUE;
  286. cur_thread->thread_profiled_own = TRUE;
  287. if (profile_thread_id == THREAD_NULL)
  288. profile_thread_id = kernel_thread(current_task(), profile_thread);
  289. } else {
  290. if (!cur_thread->thread_profiled_own)
  291. cur_thread->thread_profiled = FALSE;
  292. if (!cur_thread->thread_profiled)
  293. return KERN_SUCCESS;
  294. send_last_sample_buf(cur_thread);
  295. /* Stop profiling this thread, do the cleanup. */
  296. cur_thread->thread_profiled_own = FALSE;
  297. cur_thread->thread_profiled = FALSE;
  298. dealloc_pbuf_area(cur_thread->profil_buffer);
  299. cur_thread->profil_buffer = NULLPBUF;
  300. }
  301. return KERN_SUCCESS;
  302. }
  303. kern_return_t
  304. mach_sample_task (task, reply, cur_task)
  305. ipc_space_t task;
  306. ipc_object_t reply;
  307. task_t cur_task;
  308. {
  309. prof_data_t pbuf=cur_task->profil_buffer;
  310. vm_offset_t vmpbuf;
  311. int turnon = (reply != MACH_PORT_NULL);
  312. if (turnon) {
  313. if (cur_task->task_profiled) {
  314. if (cur_task->profil_buffer->prof_port == reply)
  315. return KERN_SUCCESS;
  316. (void) mach_sample_task(task, MACH_PORT_NULL, cur_task);
  317. }
  318. if (pbuf == NULLPBUF) {
  319. alloc_pbuf_area(pbuf, vmpbuf);
  320. if (pbuf == NULLPBUF) {
  321. return KERN_RESOURCE_SHORTAGE;
  322. }
  323. cur_task->profil_buffer = pbuf;
  324. }
  325. if (!set_pbuf_nb(pbuf, NB_PROF_BUFFER-1)) {
  326. return KERN_FAILURE;
  327. }
  328. reset_pbuf_area(pbuf);
  329. pbuf->prof_port = reply;
  330. }
  331. if (turnon != cur_task->task_profiled) {
  332. int actual,i,sentone;
  333. thread_t thread;
  334. if (turnon && profile_thread_id == THREAD_NULL)
  335. profile_thread_id =
  336. kernel_thread(current_task(), profile_thread);
  337. cur_task->task_profiled = turnon;
  338. actual = cur_task->thread_count;
  339. sentone = 0;
  340. for (i=0, thread=(thread_t) queue_first(&cur_task->thread_list);
  341. i < actual;
  342. i++, thread=(thread_t) queue_next(&thread->thread_list)) {
  343. if (!thread->thread_profiled_own) {
  344. thread->thread_profiled = turnon;
  345. if (turnon)
  346. thread->profil_buffer = cur_task->profil_buffer;
  347. else if (!sentone) {
  348. send_last_sample_buf(thread);
  349. sentone = 1;
  350. }
  351. }
  352. }
  353. if (!turnon) {
  354. dealloc_pbuf_area(pbuf);
  355. cur_task->profil_buffer = NULLPBUF;
  356. }
  357. }
  358. return KERN_SUCCESS;
  359. }
  360. #endif /* 0 */