From fd0a8b402f25ba3310e3be3264414bc7bdc4788e Mon Sep 17 00:00:00 2001 From: malc Date: Sun, 8 Jul 2007 17:37:25 +0400 Subject: [PATCH] v0.97 --- Changes | 13 +++ OMakefile | 8 +- README | 55 +++++++++--- apc.ml | 71 +++++++++------ ml_apc.c | 7 +- mod/itc-mod.c | 275 ++++++++++++++++++++++++++++++++++++++-------------------- 6 files changed, 286 insertions(+), 143 deletions(-) diff --git a/Changes b/Changes index 9180220..81696e7 100644 --- a/Changes +++ b/Changes @@ -1,3 +1,16 @@ +7 + * Stop collecting information upon release + Start upon open + + * Set itc->sleeping when entering `itc_idle' + + * Refuse to build on 2.4 + SMP + + * Fix some command line options behavior/documentation + + * Add (manually selectable) hack to work around issues + with 2.6.8-2-686-smp kernel (from Debian) + 6 * Linux/PPC(6xx) support diff --git a/OMakefile b/OMakefile index fc70de9..b384a56 100644 --- a/OMakefile +++ b/OMakefile @@ -1,4 +1,4 @@ -version = 0.96 +version = 0.97 ocaml-includes = -I +lablGL @@ -15,18 +15,18 @@ ocamlopt-libs = $(addsuffix .cmxa, $(ocaml-libs)) if $(target-win32) target-flags += -I. %.obj: %.c :value: $(c-digest-deps) :value: $(c-emit-stdmake-rule $@) - $(ocamlc) -ccopt $(quote -c $(target-flags) $(c-cflags)) $< + $(ocamlc) -ccopt $(string -c $(target-flags) $(c-cflags)) $< ml_apc.obj: else target-flags += -Wno-long-long -I. .SCANNER: %.o.scan: %.c - $(ocamlc) -ccopt $(quote $(c-cflags) \ + $(ocamlc) -ccopt $(string $(c-cflags) \ -MT $* -M -MG $(target-flags)) $< %.o: %.c :scanner: %.o.scan \ :value: $(c-digest-deps) :value: $(c-emit-stdmake-rule $@) - $(ocamlc) -ccopt $(quote -c $(target-flags) $(c-cflags)) $< + $(ocamlc) -ccopt $(string -c $(target-flags) $(c-cflags)) $< ml_apc.o: diff --git a/README b/README index 6248267..a29cd59 100644 --- a/README +++ b/README @@ -1,4 +1,27 @@ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +WARNING + +The kernel module part of this program messes with internal affairs of +the kernel, while best effort was put into making it safe, there are: + NO GUARANTEES WHATSOEVER + +Furthermore removing the previous versions of the module (via +rmmod(8)) caused one particular kernel version to panic +(2.6.8-2-686-SMP form Debian), to the best of my current knowledge +panics are only possible on SMP machines (and with maxcpus > 1). Pair +of safety nets were added and this particular kernel no longer panics +upon module removal, but, again, three words in caps above apply. + +The module expects certain things not to happen at particular point in +execution, otherwise the information kernel module exports can not be +trusted. Those `things' did happen on aforementioned Debian kernel, +so if you need to run APC there you might want to uncomment first line +of `mod/itc-mod.c'. + +2.4 series of kernels were never tested on SMP as such the module will +refuse to build for them. + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ This is APC - graphical CPU load meter. It is more suitable/accurate in situations where applications generate @@ -19,12 +42,12 @@ you will see in top(1) (or anything `/proc/stat' based) would not represent reality accurately. Ditto for plain video clips playing at 25 fps. -When frequency of aforementioned bursts devides HZ value evenly +When frequency of aforementioned bursts divides HZ value evenly reading `/proc/stat' or `/proc/uptime' can make an impression that sometimes machine gets loaded for a brief period of time but then goes idle again (for a while) then the cycle repeats. This is not the case, the machine is constantly loaded (well according to ad-hoc measuring -via background niced process and/or APC) +via background "niced" process and/or APC) If this line of thinking is correct one can not notice any load at all while watching NTSC content (30fps - does not divide 100/250/1000 @@ -47,9 +70,11 @@ tunable parameters. Tested on[2]: Linux 2.4.30 - AMD Athlon(tm) Processor (1.4 Ghz) -Linux 2.6.17.6 - AMD Athlon(tm)64 X2 Dual Core Processor 3800+ +Linux 2.6.17.6 +Linux 2.6.19.2 - AMD Athlon(tm)64 X2 Dual Core Processor 3800+ Linux 2.6.18 - AMD Athlon(tm)64 3800+ Linux 2.6.18.3 - PowerPC 7447A +Linux 2.6.19 - [some Core 2 Duo] It's possible that RMClock[3] does something similar(load measuring wise) on Microsoft Windows. @@ -75,10 +100,18 @@ Process: $ sh build.sh -$ cd mod -$ su -c 'insmod ./its.ko' - 2.6 Kernels -$ su -c 'insmod ./its.o' - 2.4 Kernels +# if following fails on X86 read bellow +$ su -c 'insmod mod/its.ko' - 2.6 Kernels +$ su -c 'insmod mod/its.o' - 2.4 Kernels + +$ major=$(awk '/ itc$/ {print $1}' /proc/devices) +$ su -c "mknod -m 0444 itc c $major 0" + +[make sure you are in X] +$ ./apc + +`````````````````````````````````````````````````````````````````````` Following applies only to Linux running on X86. If the module fails to load consult dmesg(8). Most likely cause is the @@ -93,17 +126,11 @@ Variant 2 (DANGEROUS) ------------------------------------------------------------------ Kernel 2.6 $ func=$(awk '/default_idle$/ {print "0x" $1}' /proc/kallsyms) - $ su -c "insmod ./itc.ko idle_func=$func" + $ su -c "/sbin/insmod ./itc.ko idle_func=$func" ------------------------------------------------------------------ Kernel 2.4 $ func=$(awk '/default_idle$/ {print "0x" $1}' /proc/ksyms) - $ su -c "insmod ./itc.o idle_func=$func" + $ su -c "/sbin/insmod ./itc.o idle_func=$func" ====================================================================== -$ cd .. -$ major=$(awk '/ itc$/ {print $1}' /proc/devices) -$ su -c "mknod -m 0444 itc c $major 0" - -[make sure you are in X] -$ ./apc diff --git a/apc.ml b/apc.ml index 3d3eac1..3880ae0 100644 --- a/apc.ml +++ b/apc.ml @@ -70,7 +70,9 @@ module NP = struct try String.index_from s pos ' ' with Not_found -> slen in - let i = endpos - pos |> String.sub s pos |> int_of_string in + let i = endpos - pos |> String.sub s pos + |> int_of_string + |> jiffies_to_sec in if endpos = slen then `last i @@ -88,7 +90,7 @@ module NP = struct let vals = List.rev |< if List.length vals < 7 then - 0 :: 0 :: 0 :: 0 :: vals + 0.0 :: 0.0 :: 0.0 :: 0.0 :: vals else vals in @@ -102,14 +104,13 @@ module NP = struct let rec convert accu total n = if n = nprocs then - let t = total *. hz |> truncate in + let t = total in let a = "cpu", Array.make 7 t in a :: List.rev accu else let i = Array.get ia n in let total = total +. i in - let t = i *. hz |> truncate in - let v = "cpu" ^ string_of_int n, Array.make 7 t in + let v = "cpu" ^ string_of_int n, Array.make 7 i in convert |< v :: accu |< total |< succ n in convert [] 0.0 0 @@ -134,7 +135,7 @@ end module Args = struct let banner = - [ "Amazing Piece of Code by insanely gifted programmer, Version 0.95" + [ "Amazing Piece of Code by insanely gifted programmer, Version 0.97" ; "Motivation by: gzh and afs" ; "usage: " ] |> String.concat "\n" @@ -175,10 +176,11 @@ module Args = struct ~dst_pos:0; d + let sooo b = if b then "on" else "off" let dA tos s {contents=v} = s ^ " (" ^ tos v ^ ")" let dF = dA |< sprintf "%4.2f" - let dB = dA string_of_bool - let dcB = dA (fun b -> not b |> string_of_bool) + let dB = dA sooo + let dcB = dA sooo let dI = dA string_of_int let dS = dA (fun s -> "`" ^ String.escaped s ^ "'") @@ -191,19 +193,23 @@ module Args = struct let sB opt r doc = "-" ^ opt, Arg.Set r, pad 9 "" ^ doc |> dB |< r - let cB opt r doc = - "-" ^ opt, Arg.Clear r, pad 9 "" ^ doc |> dcB |< r - let sS opt r doc = "-" ^ opt, Arg.Set_string r, pad 9 " " ^ doc |> dS |< r + let fB opt r doc = + if r.contents + then + "-" ^ opt, Arg.Clear r, pad 9 "" ^ doc |> dB |< r + else + "-" ^ opt, Arg.Clear r, pad 9 "" ^ doc |> dcB |< r + let init () = let opts = [ sF "f" freq "sampling frequency in seconds" ; sF "D" delay "refresh delay in seconds" ; sF "i" interval "history interval in seconds" - ; sI "p" pgrid "percent grid" - ; sI "s" sgrid "history grid" + ; sI "p" pgrid "percent grid items" + ; sI "s" sgrid "history grid items" ; sI "w" w "width" ; sI "h" h "height" ; sI "b" barw "bar width" @@ -211,18 +217,18 @@ module Args = struct ; sI "n" niceval "value to renice self on init" ; sI "t" timer "timer frequency in herz" ; sS "d" devpath "path to itc device" - ; cB "k" ksampler |< "do not use kernel sampler" + ; fB "k" ksampler |< "kernel sampler" ^ (if NP.winnt then "" else " (`/proc/[stat|uptime]')") - ; sB "g" gzh "gzh way (does not quite work yet)" - ; sB "u" uptime - "use `uptime' instead of `stat' as kernel sampler (UP only)" + ; fB "g" gzh "gzh way (does not quite work yet)" + ; fB "u" uptime + "`uptime' instead of `stat' as kernel sampler (UP only)" ; sB "v" verbose "verbose" - ; sB "S" sigway "sigwait delay method" - ; sB "c" scalebar "constant bar width" - ; sB "P" poly "use filled area instead of lines" - ; sB "I" icon "use icon (hack)" - ; cB "l" labels "do not draw labels" - ; sB "m" mgrid "moving grid" + ; fB "S" sigway "sigwait delay method" + ; fB "c" scalebar "constant bar width" + ; fB "P" poly "filled area instead of lines" + ; fB "I" icon "icon (hack)" + ; fB "l" labels "labels" + ; fB "m" mgrid "moving grid" ] in let opts = @@ -884,8 +890,8 @@ let create fd w h = let i1 = g ks |> ref in fun ks t1 t2 -> let i2 = g ks in - let i1' = NP.jiffies_to_sec !i1 - and i2' = NP.jiffies_to_sec i2 in + let i1' = !i1 + and i2' = i2 in i1 := i2; (i1', i2') in @@ -912,9 +918,13 @@ let create fd w h = let i1 = Array.get is i |> ref in fun is t1 t2 -> let i2 = Array.get is i in - let i1' = !i1 in - i1 := i2; - (i1', i2) + if classify_float i2 = FP_infinite + then + (t1, t2) + else + let i1' = !i1 in + i1 := i2; + (i1', i2) in let kaccu = if !Args.ksampler @@ -1004,6 +1014,11 @@ let seticon () = let main () = let _ = Glut.init [|""|] in let () = Args.init () in + let () = + if !Args.verbose + then + "detected " ^ string_of_int NP.nprocs ^ " CPUs" |> print_endline + in let () = if !Args.gzh then Gzh.init !Args.verbose in let () = Delay.init !Args.timer !Args.gzh in let () = if !Args.niceval != 0 then NP.setnice !Args.niceval in diff --git a/ml_apc.c b/ml_apc.c index 43c8722..3704663 100644 --- a/ml_apc.c +++ b/ml_apc.c @@ -8,6 +8,7 @@ #include #include +#include #include #include @@ -124,8 +125,9 @@ CAMLprim value ml_idletimeofday (value fd_v, value nprocs_v) res_v = caml_alloc (nprocs * Double_wosize, Double_array_tag); for (i = 0; i < nprocs; ++i) { - double d = buf[i].tv_sec + buf[i].tv_usec * 1e-6; + double d; + d = buf[i].tv_sec + buf[i].tv_usec * 1e-6; Store_double_field (res_v, i, d); } CAMLreturn (res_v); @@ -197,6 +199,9 @@ CAMLprim value ml_seticon (value data_v) } } } + else { + CAMLreturn (Val_unit); + } p[0] = 32; p[1] = 32; diff --git a/mod/itc-mod.c b/mod/itc-mod.c index 0676771..8135ee7 100644 --- a/mod/itc-mod.c +++ b/mod/itc-mod.c @@ -1,3 +1,5 @@ +/* #define ITC_PREEMPT_HACK */ +/* uncomment the above if APC blatantly lies and PREEMPTION is enabled */ #include #include #include @@ -26,10 +28,14 @@ #if LINUX_VERSION_CODE < KERNEL_VERSION (2, 6, 0) #include + #include #ifdef CONFIG_SMP - #define NB_CPUS weight (phys_cpu_present_map) + #error Support for SMP on 2.4 series of kernels is not written yet #else - #define NB_CPUS 1 + #define num_present_cpus() 1 + #define num_online_cpus() 1 + /* #define cpu_online(n) 1 */ + #define cpu_present(n) 1 #endif #if LINUX_VERSION_CODE < KERNEL_VERSION (2, 4, 20) @@ -37,10 +43,24 @@ #else #define iminor(inode) minor((inode)->i_rdev) #endif -#else - #define NB_CPUS num_present_cpus () #endif +#ifdef CONFIG_PREEMPT +#define itc_enter_bkl() do { \ + preempt_disable (); \ + lock_kernel (); \ +} while (0) +#define itc_leave_bkl() do { \ + unlock_kernel (); \ + preempt_enable (); \ +} while (0) +#else +#define itc_enter_bkl lock_kernel +#define itc_leave_bkl unlock_kernel +#ifdef ITC_PREEMPT_HACK +#error Attempt to enable ITC_PREEMPT_HACK on non preemptible kernel +#endif +#endif MODULE_DESCRIPTION ("Idle time collector"); @@ -100,6 +120,98 @@ cpeamb (struct timeval *c, struct timeval *a, struct timeval *b) } } +#if LINUX_VERSION_CODE >= KERNEL_VERSION (2, 6, 0) +/* XXX: 2.4 */ +/********************************************************************** + * + * Dummy to make sure we are unloaded properly + * + **********************************************************************/ +static void +dummy_wakeup (void *unused) +{ + printk (KERN_DEBUG "waking up %d\n", smp_processor_id ()); + /* needed? safe? */ + set_need_resched (); +} +#endif + +/********************************************************************** + * + * idle + * + **********************************************************************/ +#if LINUX_VERSION_CODE > KERNEL_VERSION (2, 6, 0) +#ifndef CONFIG_APM +#define QUIRK +#endif +#else +void default_idle (void); +#endif + +static void +itc_monotonic (struct timeval *tv) +{ + do_gettimeofday (tv); +} + +static void +itc_idle (void) +{ + struct itc *itc; + struct timeval tv; + unsigned long flags; + +#ifdef ITC_PREEMPT_HACK + preempt_disable (); +#endif + + /* printk ("idle in %d\n", smp_processor_id ()); */ + spin_lock_irqsave (&lock, flags); + itc = &global_itc[smp_processor_id ()]; + itc_monotonic (&itc->sleep_started); + itc->sleeping = 1; + spin_unlock_irqrestore (&lock, flags); + +#ifdef QUIRK + if (orig_pm_idle) + { + orig_pm_idle (); + } + else + { + idle_func (); + } +#else + if (orig_pm_idle) + { + orig_pm_idle (); + } + else + { + if (idle_func) + { + idle_func (); + } + else + { + default_idle (); + } + } +#endif + + spin_lock_irqsave (&lock, flags); + itc_monotonic (&tv); + cpeamb (&itc->cumm_sleep_time, &tv, &itc->sleep_started); + itc->sleeping = 0; + spin_unlock_irqrestore (&lock, flags); + /* printk ("idle out %d\n", smp_processor_id ()); */ + +#ifdef ITC_PREEMPT_HACK + preempt_enable (); +#endif +} + /********************************************************************** * * File operations @@ -128,10 +240,16 @@ static struct file_operations itc_fops = .read = itc_read, }; - static int itc_release (struct inode * inode, struct file * filp) { + itc_enter_bkl (); + pm_idle = orig_pm_idle; + itc_leave_bkl (); +#if LINUX_VERSION_CODE >= KERNEL_VERSION (2, 6, 0) + /* XXX: 2.4 */ + on_each_cpu (dummy_wakeup, NULL, 0, 1); +#endif return 0; } @@ -143,11 +261,22 @@ itc_open (struct inode * inode, struct file * filp) unsigned int minor = iminor (inode); if (minor != 0) - return -ENODEV; + { + return -ENODEV; + } /* old_fops = filp->f_op; */ filp->f_op = fops_get (&itc_fops); fops_put (old_fops); + + itc_enter_bkl (); + if (pm_idle != itc_idle) + { + orig_pm_idle = pm_idle; + } + pm_idle = itc_idle; + itc_leave_bkl (); + return ret; } @@ -160,39 +289,43 @@ itc_read (struct file *file, char * buf, size_t count, loff_t * ppos) unsigned long flags; struct itc *itc = &global_itc[0]; - /* printk ("itemsize=%d cpus=%d count=%d\n", itemsize, NR_CPUS, count); */ - if (count < itemsize * NB_CPUS) + if (count < itemsize * num_present_cpus ()) { printk (KERN_ERR "attempt to read something funny %d expected %d(%d,%d)\n", - count, itemsize * NB_CPUS, itemsize, NB_CPUS); + count, itemsize * num_present_cpus (), + itemsize, num_present_cpus ()); return -EINVAL; } spin_lock_irqsave (&lock, flags); - for (i = 0; i < NB_CPUS; ++i, ++itc) + for (i = 0; i < NR_CPUS; ++i, ++itc) { - if (itc->sleeping) + if (cpu_present (i)) { - struct timeval tv; - - do_gettimeofday (&tv); - cpeamb (&itc->cumm_sleep_time, &tv, &itc->sleep_started); - itc->sleep_started.tv_sec = tv.tv_sec; - itc->sleep_started.tv_usec = tv.tv_usec; + if (itc->sleeping) + { + struct timeval tv; + + itc_monotonic (&tv); + cpeamb (&itc->cumm_sleep_time, &tv, &itc->sleep_started); + itc->sleep_started.tv_sec = tv.tv_sec; + itc->sleep_started.tv_usec = tv.tv_usec; + } + + if (copy_to_user (buf, &itc->cumm_sleep_time, itemsize)) + { + printk (KERN_ERR "failed to write %zu bytes to %p\n", + itemsize, buf); + retval = -EFAULT; + break; + } + retval += itemsize; + buf += itemsize; } - - if (copy_to_user (buf, &itc->cumm_sleep_time, itemsize)) - { - printk (KERN_ERR "failed to write %zu bytes to %p\n", itemsize, buf); - retval = -EFAULT; - break; - } - retval += itemsize; - buf += itemsize; } - spin_unlock_irqrestore (&lock, flags); + return retval; } @@ -205,68 +338,7 @@ static int itc_ioctl (struct inode * inode, struct file * filp, unsigned int cmd, unsigned long arg) { - return 0; -} - -/********************************************************************** - * - * idle - * - **********************************************************************/ -#if LINUX_VERSION_CODE > KERNEL_VERSION (2, 6, 0) -#ifndef CONFIG_APM -#define QUIRK -#endif -#else -void default_idle (void); -#endif - -static void -itc_idle (void) -{ - struct itc *itc; - struct timeval tv; - unsigned long flags; - - /* printk ("idle in\n"); */ - spin_lock_irqsave (&lock, flags); - itc = &global_itc[smp_processor_id ()]; - do_gettimeofday (&itc->sleep_started); - spin_unlock_irqrestore (&lock, flags); - -#ifdef QUIRK - if (orig_pm_idle) - { - orig_pm_idle (); - } - else - { - idle_func (); - } -#else - if (orig_pm_idle) - { - orig_pm_idle (); - } - else - { - if (idle_func) - { - idle_func (); - } - else - { - default_idle (); - } - } -#endif - - spin_lock_irqsave (&lock, flags); - do_gettimeofday (&tv); - cpeamb (&itc->cumm_sleep_time, &tv, &itc->sleep_started); - itc->sleeping = 0; - spin_unlock_irqrestore (&lock, flags); - /* printk ("idle out\n"); */ + return -EINVAL; } /********************************************************************** @@ -304,6 +376,7 @@ init (void) itc_major = err; } + orig_pm_idle = pm_idle; printk (KERN_DEBUG "itc: driver loaded pm_idle=%p default_idle=%p, idle_func=%p\n", @@ -315,9 +388,21 @@ init (void) #endif idle_func ); - - orig_pm_idle = pm_idle; - pm_idle = itc_idle; + printk (KERN_DEBUG "itc: CPUs(%d present=%d online=%d)" +#ifdef QUIRK + " Q" +#endif +#ifdef CONFIG_APM + " A" +#endif +#ifdef CONFIG_SMP + " S" +#endif +#ifdef CONFIG_PREEMPT + " P" +#endif + "\n", + NR_CPUS, num_present_cpus (), num_online_cpus ()); return 0; } @@ -329,12 +414,10 @@ init (void) static __exit void fini (void) { - printk (KERN_DEBUG "itc: unloading\n"); - + printk (KERN_DEBUG "itc: unloading (resetting pm_idle to %p)\n", + orig_pm_idle); unregister_chrdev (itc_major, DEVNAME); printk (KERN_DEBUG "itc: unloaded\n"); - - pm_idle = orig_pm_idle; } module_init (init); -- 2.11.4.GIT