/* * %CopyrightBegin% * * Copyright Ericsson AB 2001-2012. All Rights Reserved. * * The contents of this file are subject to the Erlang Public License, * Version 1.1, (the "License"); you may not use this file except in * compliance with the License. You should have received a copy of the * Erlang Public License along with this software. If not, it can be * retrieved online at http://www.erlang.org/. * * Software distributed under the License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See * the License for the specific language governing rights and limitations * under the License. * * %CopyrightEnd% */ /* * hipe_bif1.c * * Performance analysis support. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "sys.h" #include "global.h" #include "bif.h" #include "big.h" #include "error.h" #include "beam_load.h" #include "hipe_bif0.h" #include "hipe_bif1.h" #define BeamOpCode(Op) ((Uint)BeamOp(Op)) BIF_RETTYPE hipe_bifs_call_count_on_1(BIF_ALIST_1) { Eterm *pc; struct hipe_call_count *hcc; pc = hipe_bifs_find_pc_from_mfa(BIF_ARG_1); if (!pc) BIF_ERROR(BIF_P, BADARG); ASSERT(pc[-5] == BeamOpCode(op_i_func_info_IaaI)); if (pc[0] == BeamOpCode(op_hipe_trap_call)) BIF_ERROR(BIF_P, BADARG); if (pc[0] == BeamOpCode(op_hipe_call_count)) BIF_RET(NIL); hcc = erts_alloc(ERTS_ALC_T_HIPE, sizeof(*hcc)); hcc->count = 0; hcc->opcode = pc[0]; pc[-4] = (Eterm)hcc; pc[0] = BeamOpCode(op_hipe_call_count); BIF_RET(am_true); } BIF_RETTYPE hipe_bifs_call_count_off_1(BIF_ALIST_1) { Eterm *pc; struct hipe_call_count *hcc; unsigned count; pc = hipe_bifs_find_pc_from_mfa(BIF_ARG_1); if (!pc) BIF_ERROR(BIF_P, BADARG); ASSERT(pc[-5] == BeamOpCode(op_i_func_info_IaaI)); if (pc[0] != BeamOpCode(op_hipe_call_count)) BIF_RET(am_false); hcc = (struct hipe_call_count*)pc[-4]; count = hcc->count; pc[0] = hcc->opcode; pc[-4] = (Eterm)NULL; erts_free(ERTS_ALC_T_HIPE, hcc); BIF_RET(make_small(count)); } BIF_RETTYPE hipe_bifs_call_count_get_1(BIF_ALIST_1) { Eterm *pc; struct hipe_call_count *hcc; pc = hipe_bifs_find_pc_from_mfa(BIF_ARG_1); if (!pc) BIF_ERROR(BIF_P, BADARG); ASSERT(pc[-5] == BeamOpCode(op_i_func_info_IaaI)); if (pc[0] != BeamOpCode(op_hipe_call_count)) BIF_RET(am_false); hcc = (struct hipe_call_count*)pc[-4]; BIF_RET(make_small(hcc->count)); } BIF_RETTYPE hipe_bifs_call_count_clear_1(BIF_ALIST_1) { Eterm *pc; struct hipe_call_count *hcc; unsigned count; pc = hipe_bifs_find_pc_from_mfa(BIF_ARG_1); if (!pc) BIF_ERROR(BIF_P, BADARG); ASSERT(pc[-5] == BeamOpCode(op_i_func_info_IaaI)); if (pc[0] != BeamOpCode(op_hipe_call_count)) BIF_RET(am_false); hcc = (struct hipe_call_count*)pc[-4]; count = hcc->count; hcc->count = 0; BIF_RET(make_small(count)); } unsigned int hipe_trap_count; BIF_RETTYPE hipe_bifs_trap_count_get_0(BIF_ALIST_0) { BIF_RET(make_small(hipe_trap_count)); } BIF_RETTYPE hipe_bifs_trap_count_clear_0(BIF_ALIST_0) { unsigned int count = hipe_trap_count; hipe_trap_count = 0; BIF_RET(make_small(count)); } /***************************************************************************** * BIFs for benchmarking. These only do useful things if * __BENCHMARK__ is defined in beam/benchmark.h. For documentation * about how to add new counters or maintain the existing counters, * see benchmark.h. * * If benchmarking is not enabled all BIFs will return false. If the * required benchmark feature is not enabled, the counter will remain * zero. * * process_info/0 -> { Number of live processes, * Processes spawned in total } * * Live processes are increased when a new process is created, and * decreased when a process dies. Processes spawned is increased * when a process is created. * * * process_info_clear/0 -> true * * Will reset the processes spawned-counters to zero. If this is * done at some improper time, live processes may become a negative * value. This is not a problem in itself, just as long as you know * about it. * * * message_info/0 -> { Messages sent, * Messages copied, * Ego messages (sender = receiver), * Words sent, * Words copied, * Words preallocated } * * Counting the words sent in a shared heap system will affect * runtime performance since it means that we have to calculate the * size of the mesage. With private heaps, this is done anyway and * will not affect performance. * * * message_info_clear/0 -> true * * Reset the message counters to zero. * * * message_sizes/0 -> true * * Displays a text-mode bar diagram with message sizes. There are no * guaranties that this is printed in a way the Erlang system is * supposed to print things. * * * gc_info/0 -> { Minor collections, * Major collections, * Used heap, * Allocated heap, * Max used heap, * Max allocated heap } * * Information about private heap garbage collections. Number of * minor and major collections, how much heap is used and allocated * and how much heap has been in use and allocated at most since the * counters were reset. * * * shared_gc_info/0 -> { Minor collections of the shared heap, * Major collections of the shared heap, * Used shared heap, * Allocated shared heap, * Max used shared heap, * Max allocated shared heap } * * The same as above, but for the shared heap / message area. Note, * that in a shared heap system the max used heap and max allocated * heap are mostly the same, since the heap allways is filled before * a garbage collection, and most garbage collections do not enlarge * the heap. The private heap numbers are much more interesting. * * * incremental_gc_info/0 -> { Complete minor GC cycles, * Complete major GC cycles, * Minor GC stages, * Major GC stages } * * * gc_info_clear/0 -> true * * Reset counters for both private and shared garbage collection. * * * BM Timers * --------- * * All timers returns tuples of the kind: { Minutes, Seconds, Milliseconds } * except for the max times in garbage collection where times are normally * small. The tuple is therefor: { Seconds, Milliseconds, Microseconds } * * system_timer/0 -> Mutator time * * This timer is not a real-time clock, it only runs when a process * is scheduled to run. You can not find out the accual time a * program has taken to run using this timer. * * * system_timer_clear/0 -> true * * Reset system timer to zero. * * * send_timer/0 -> { Send time, * Copy time, * Size time } * * Time spent in sending messages. The copy time and size time are * only active if the copying is needed in send. Copying of data * into ETS-tables etc is not timed with this timer. * * * send_timer_clear/0 -> true * * Reset send timers to zero. * * * gc_timer/0 -> { Time in minor collection, * Time in major collection, * Max time in minor collection (�s), * Max time in major collection (�s) } * * Total time spent in garbage collection of the private heaps. The * max times are for one separate collection. * * * shared_gc_timer/0 -> { Time in minor collection, * Time in major collection, * Max time in minor collection (�s), * Max time in major collection (�s) } * * Total time spent in garbage collection of the shared heap / * message area. The max times are for one separate collection. * * * gc_timer_clear/0 -> true * * Reset private and shared garbage collection timers to zero. Note, * that the max-times are also reset. * * * misc_timer/0 -> { Misc 0, Misc 1, Misc 2 } * * Timers for debug purposes. In a normal system, these timers are * never used. Add these timers at places where you want to time * something not covered here. Use BM_SWAP_TIMER(from,to) to start * one of the misc timers. * * ... code timed by the system timer ... * BM_SWAP_TIMER(system,misc1); * ... code we want to time ... * BM_SWAP_TIMER(misc1,system); * ... back on system time ... * * * misc_timer_clear/0 -> true * * Reset misc timers to zero. */ BIF_RETTYPE hipe_bifs_process_info_0(BIF_ALIST_0) { #ifdef __BENCHMARK__ #ifndef BM_COUNTERS Uint processes_busy = 0; Uint processes_spawned = 0; #endif Eterm *hp; hp = HAlloc(BIF_P, 3); BIF_RET(TUPLE2(hp, make_small(processes_busy), make_small(processes_spawned))); #else BIF_RET(am_false); #endif } BIF_RETTYPE hipe_bifs_process_info_clear_0(BIF_ALIST_0) { #ifdef __BENCHMARK__ #ifdef BM_COUNTERS processes_spawned = 0; #endif BIF_RET(am_true); #else BIF_RET(am_false); #endif } BIF_RETTYPE hipe_bifs_message_info_0(BIF_ALIST_0) { #ifdef __BENCHMARK__ Eterm *hp; #ifndef BM_COUNTERS unsigned long messages_sent = 0; unsigned long messages_copied = 0; unsigned long messages_ego = 0; #endif #ifndef BM_MESSAGE_SIZES unsigned long words_sent = 0; unsigned long words_copied = 0; unsigned long words_prealloc = 0; #endif hp = HAlloc(BIF_P, 7); BIF_RET(TUPLE6(hp, make_small(messages_sent), make_small(messages_copied), make_small(messages_ego), make_small(words_sent), make_small(words_copied), make_small(words_prealloc))); #else BIF_RET(am_false); #endif } BIF_RETTYPE hipe_bifs_message_info_clear_0(BIF_ALIST_0) { #ifdef __BENCHMARK__ #ifdef BM_COUNTERS messages_sent = 0; messages_copied = 0; messages_ego = 0; #endif #ifdef BM_MESSAGE_SIZES words_sent = 0; words_copied = 0; words_prealloc = 0; { int i; for (i = 0; i < 1000; i++) message_sizes[i] = 0; } #endif BIF_RET(am_true); #else BIF_RET(am_false); #endif } BIF_RETTYPE hipe_bifs_message_sizes_0(BIF_ALIST_0) { #ifdef BM_MESSAGE_SIZES int i, j, max = 0; int tmp[12] = {0,0,0,0,0,0,0,0,0,0,0,0}; for (i = 0; i < 65; i++) { tmp[0] += message_sizes[i]; if (tmp[0] > max) max = tmp[0]; } for (i = 65; i < 999; i++) { tmp[i / 100 + 1] += message_sizes[i]; if (tmp[i / 100 + 1] > max) max = tmp[i / 100 + 1]; } tmp[11] = message_sizes[999]; if (tmp[11] > max) max = tmp[11]; for (i = -1; i < 11; i++) { int num = (tmp[i + 1] * 50) / max; if (i == -1) printf("\n\r 0 - 64: (%6d) |", tmp[0]); else if (i == 0) printf("\n\r 65 - 99: (%6d) |", tmp[1]); else if (i == 10) printf("\n\r >= 1000: (%6d) |", tmp[11]); else printf("\n\r%3d - %3d: (%6d) |", i * 100, i * 100 + 99, tmp[i + 1]); for (j = 0; j < num; j++) printf("."); } printf("\n\r"); BIF_RET(am_true); #else BIF_RET(am_false); #endif } BIF_RETTYPE hipe_bifs_gc_info_0(BIF_ALIST_0) { #ifdef __BENCHMARK__ #ifndef BM_COUNTERS Uint minor_gc = 0; Uint major_gc = 0; #endif #ifndef BM_HEAP_SIZES Uint max_used_heap = 0; Uint max_allocated_heap = 0; #endif Eterm *hp; Uint used_heap = (BIF_P->htop - BIF_P->heap) + (OLD_HTOP(BIF_P) - OLD_HEAP(BIF_P)) + MBUF_SIZE(BIF_P); Uint alloc_heap = (BIF_P->hend - BIF_P->heap) + (OLD_HEND(BIF_P) - OLD_HEAP(BIF_P)) + MBUF_SIZE(BIF_P); hp = HAlloc(BIF_P, 7); BIF_RET(TUPLE6(hp, make_small((Uint)minor_gc), make_small((Uint)major_gc), make_small((Uint)used_heap), make_small((Uint)alloc_heap), make_small(max_used_heap), make_small(max_allocated_heap))); #else BIF_RET(am_false); #endif } BIF_RETTYPE hipe_bifs_shared_gc_info_0(BIF_ALIST_0) { #ifdef __BENCHMARK__ #if !(defined(BM_COUNTERS)) Uint minor_global_gc = 0; Uint major_global_gc = 0; #endif #ifndef BM_HEAP_SIZES Uint max_used_global_heap = 0; Uint max_allocated_global_heap = 0; #endif Eterm *hp; Uint tmp_used_heap = 0; Uint tmp_allocated_heap = 0; hp = HAlloc(BIF_P, 7); BIF_RET(TUPLE6(hp, make_small((uint)minor_global_gc), make_small((uint)major_global_gc), make_small(tmp_used_heap), make_small(tmp_allocated_heap), make_small(max_used_global_heap), make_small(max_allocated_global_heap))); #else BIF_RET(am_false); #endif } BIF_RETTYPE hipe_bifs_incremental_gc_info_0(BIF_ALIST_0) { #ifdef __BENCHMARK__ #if !defined(BM_COUNTERS) Uint minor_gc_cycles = 0; Uint major_gc_cycles = 0; Uint minor_gc_stages = 0; Uint major_gc_stages = 0; #endif Eterm *hp; hp = HAlloc(BIF_P, 5); BIF_RET(TUPLE4(hp, make_small(minor_gc_cycles), make_small(major_gc_cycles), make_small(minor_gc_stages), make_small(major_gc_stages))); #else BIF_RET(am_false); #endif } BIF_RETTYPE hipe_bifs_gc_info_clear_0(BIF_ALIST_0) { #ifdef __BENCHMARK__ #ifdef BM_COUNTERS minor_gc = 0; major_gc = 0; #endif #ifdef BM_HEAP_SIZES max_used_heap = 0; max_allocated_heap = 0; max_used_global_heap = 0; max_allocated_global_heap = 0; #endif BIF_RET(am_true); #else BIF_RET(am_false); #endif } BIF_RETTYPE hipe_bifs_pause_times_0(BIF_ALIST_0) { #ifdef BM_TIMERS int i; int total_time = 0, n = 0; int left = 0, right = 0, mid = 0; printf("Pause times in minor collection:\r\n"); for (i = 0; i < MAX_PAUSE_TIME; i++) { if (pause_times[i] > 0) { printf("%d: %ld\r\n", i, pause_times[i]); total_time += pause_times[i] * i; n += pause_times[i]; if (i > mid) right += pause_times[i]; while (right > left) { left += pause_times[mid++]; right -= pause_times[mid]; } } } printf("Number of collections: %d\r\n", n); printf("Total collection time: %d\r\n", total_time); if (n > 0) printf("Mean pause time: %d\r\n", total_time / n); printf("Geometrical mean: %d\r\n", mid); total_time = 0; n = 0; left = 0; right = 0; mid = 0; printf("Pause times in major collection:\r\n"); for (i = 0; i < MAX_PAUSE_TIME; i++) { if (pause_times_old[i] > 0) { printf("%d: %ld\r\n", i, pause_times_old[i]); total_time += pause_times_old[i] * i; n += pause_times_old[i]; } } printf("Number of collections: %d\r\n", n); printf("Total collection time: %d\r\n", total_time); if (n > 0) printf("Mean pause time: %d\r\n", total_time / n); BIF_RET(am_true); #else BIF_RET(am_false); #endif } /* XXX: these macros have free variables */ #ifdef BM_TIMERS #if USE_PERFCTR #define MAKE_TIME(_timer_) { \ BM_TIMER_T tmp = _timer_##_time; \ milli = (uint)(tmp - ((int)(tmp / 1000)) * 1000); \ tmp /= 1000; \ sec = (uint)(tmp - ((int)(tmp / 60)) * 60); \ min = (uint)tmp / 60; } #define MAKE_MICRO_TIME(_timer_) { \ BM_TIMER_T tmp = _timer_##_time * 1000; \ micro = (uint)(tmp - ((int)(tmp / 1000)) * 1000); \ tmp /= 1000; \ milli = (uint)(tmp - ((int)(tmp / 1000)) * 1000); \ sec = (uint)tmp / 1000; } #else #define MAKE_TIME(_timer_) { \ BM_TIMER_T tmp = _timer_##_time / 1000000; \ milli = tmp % 1000; \ tmp /= 1000; \ sec = tmp % 60; \ min = tmp / 60; } #define MAKE_MICRO_TIME(_timer_) { \ BM_TIMER_T tmp = _timer_##_time / 1000; \ micro = tmp % 1000; \ tmp /= 1000; \ milli = tmp % 1000; \ sec = tmp / 1000; } #endif #else #define MAKE_TIME(_timer_) #define MAKE_MICRO_TIME(_timer_) #endif BIF_RETTYPE hipe_bifs_system_timer_0(BIF_ALIST_0) { #ifdef __BENCHMARK__ uint min = 0; uint sec = 0; uint milli = 0; Eterm *hp; hp = HAlloc(BIF_P, 4); MAKE_TIME(system); BIF_RET(TUPLE3(hp, make_small(min), make_small(sec), make_small(milli))); #else BIF_RET(am_false); #endif } BIF_RETTYPE hipe_bifs_system_timer_clear_0(BIF_ALIST_0) { #ifdef BM_TIMERS system_time = 0; BIF_RET(am_true); #else BIF_RET(am_false); #endif } BIF_RETTYPE hipe_bifs_send_timer_0(BIF_ALIST_0) { #ifdef __BENCHMARK__ uint min = 0; uint sec = 0; uint milli = 0; Eterm *hp; Eterm sendtime, copytime, sizetime; hp = HAlloc(BIF_P, 4 * 4); MAKE_TIME(send); sendtime = TUPLE3(hp, make_small(min), make_small(sec), make_small(milli)); hp += 4; MAKE_TIME(copy); copytime = TUPLE3(hp, make_small(min), make_small(sec), make_small(milli)); hp += 4; MAKE_TIME(size); sizetime = TUPLE3(hp, make_small(min), make_small(sec), make_small(milli)); hp += 4; BIF_RET(TUPLE3(hp, sendtime, copytime, sizetime)); #else BIF_RET(am_false); #endif } BIF_RETTYPE hipe_bifs_send_timer_clear_0(BIF_ALIST_0) { #ifdef BM_TIMERS send_time = 0; copy_time = 0; size_time = 0; BIF_RET(am_true); #else BIF_RET(am_false); #endif } BIF_RETTYPE hipe_bifs_gc_timer_0(BIF_ALIST_0) { #ifdef __BENCHMARK__ Eterm *hp; uint min = 0; uint sec = 0; uint milli = 0; uint micro = 0; Eterm minor, major, max_min, max_maj; hp = HAlloc(BIF_P, 4 * 4 + 5); MAKE_TIME(minor_gc); minor = TUPLE3(hp, make_small(min), make_small(sec), make_small(milli)); hp += 4; MAKE_TIME(major_gc); major = TUPLE3(hp, make_small(min), make_small(sec), make_small(milli)); hp += 4; MAKE_MICRO_TIME(max_minor); max_min = TUPLE3(hp, make_small(sec), make_small(milli), make_small(micro)); hp += 4; MAKE_MICRO_TIME(max_major); max_maj = TUPLE3(hp, make_small(sec), make_small(milli), make_small(micro)); hp += 4; BIF_RET(TUPLE4(hp, minor, major, max_min, max_maj)); #else BIF_RET(am_false); #endif } BIF_RETTYPE hipe_bifs_shared_gc_timer_0(BIF_ALIST_0) { #ifdef __BENCHMARK__ Eterm *hp; uint min = 0; uint sec = 0; uint milli = 0; uint micro = 0; Eterm minor, major, max_min, max_maj; hp = HAlloc(BIF_P, 4 * 4 + 5); MAKE_TIME(minor_global_gc); minor = TUPLE3(hp, make_small(min), make_small(sec), make_small(milli)); hp += 4; MAKE_TIME(major_global_gc); major = TUPLE3(hp, make_small(min), make_small(sec), make_small(milli)); hp += 4; MAKE_MICRO_TIME(max_global_minor); max_min = TUPLE3(hp, make_small(sec), make_small(milli), make_small(micro)); hp += 4; MAKE_MICRO_TIME(max_global_major); max_maj = TUPLE3(hp, make_small(sec), make_small(milli), make_small(micro)); hp += 4; BIF_RET(TUPLE4(hp, minor, major, max_min, max_maj)); #else BIF_RET(am_false); #endif } BIF_RETTYPE hipe_bifs_gc_timer_clear_0(BIF_ALIST_0) { #ifdef BM_TIMERS minor_gc_time = 0; major_gc_time = 0; max_minor_time = 0; max_major_time = 0; minor_global_gc_time = 0; major_global_gc_time = 0; max_global_minor_time = 0; max_global_major_time = 0; BIF_RET(am_true); #else BIF_RET(am_false); #endif } BIF_RETTYPE hipe_bifs_misc_timer_0(BIF_ALIST_0) { #ifdef __BENCHMARK__ uint min = 0; uint sec = 0; uint milli = 0; Eterm *hp; Eterm misctime1, misctime2, misctime3; hp = HAlloc(BIF_P, 4 * 4); MAKE_TIME(misc0); misctime1 = TUPLE3(hp, make_small(min), make_small(sec), make_small(milli)); hp += 4; MAKE_TIME(misc1); misctime2 = TUPLE3(hp, make_small(min), make_small(sec), make_small(milli)); hp += 4; MAKE_TIME(misc2); misctime3 = TUPLE3(hp, make_small(min), make_small(sec), make_small(milli)); hp += 4; BIF_RET(TUPLE3(hp, misctime1, misctime2, misctime3)); #else BIF_RET(am_false); #endif } BIF_RETTYPE hipe_bifs_misc_timer_clear_0(BIF_ALIST_0) { #ifdef BM_TIMERS misc0_time = 0; misc1_time = 0; misc2_time = 0; BIF_RET(am_true); #else BIF_RET(am_false); #endif } #undef MAKE_TIME #undef MAKE_MICRO_TIME /* * HiPE hrvtime(). * These implementations are currently available: * + On Linux with the perfctr extension we can use the process' * virtualised time-stamp counter. To enable this mode you must * pass `--with-perfctr=/path/to/perfctr' when configuring. * + The fallback, which is the same as {X,_} = runtime(statistics). */ static double fallback_get_hrvtime(void) { unsigned long ms_user; elapsed_time_both(&ms_user, NULL, NULL, NULL); return (double)ms_user; } #if USE_PERFCTR #include "hipe_perfctr.h" static int hrvtime_started; /* 0: closed, +1: perfctr, -1: fallback */ #define hrvtime_is_started() (hrvtime_started != 0) static void start_hrvtime(void) { if (hipe_perfctr_hrvtime_open() >= 0) hrvtime_started = 1; else hrvtime_started = -1; } static void stop_hrvtime(void) { if (hrvtime_started > 0) hipe_perfctr_hrvtime_close(); hrvtime_started = 0; } static double get_hrvtime(void) { if (hrvtime_started > 0) return hipe_perfctr_hrvtime_get(); else return fallback_get_hrvtime(); } #else /* !USE_PERFCTR */ /* * Fallback, if nothing better exists. * This is the same as {X,_} = statistics(runtime), which uses * times(2) on Unix systems. */ #define hrvtime_is_started() 1 #define start_hrvtime() do{}while(0) #define stop_hrvtime() do{}while(0) #define get_hrvtime() fallback_get_hrvtime() #endif /* !USE_PERFCTR */ BIF_RETTYPE hipe_bifs_get_hrvtime_0(BIF_ALIST_0) { Eterm *hp; Eterm res; FloatDef f; if (!hrvtime_is_started()) start_hrvtime(); f.fd = get_hrvtime(); hp = HAlloc(BIF_P, FLOAT_SIZE_OBJECT); res = make_float(hp); PUT_DOUBLE(f, hp); BIF_RET(res); } BIF_RETTYPE hipe_bifs_stop_hrvtime_0(BIF_ALIST_0) { stop_hrvtime(); BIF_RET(am_true); }