1 /*
2  * %CopyrightBegin%
3  *
4  * Copyright Ericsson AB 1997-2016. All Rights Reserved.
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  *
18  * %CopyrightEnd%
19  */
20 /*
21  * CPU supervision
22  *
23  * Uses kstat library only available on Solaris 2
24  * Compile with: gcc -o cpu_sup cpu_sup.c -lkstat
25  *
26  * Use open_port({spawn,Prog},[stream]) to communicate.
27  *
28  */
29 
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <unistd.h>
33 #include <string.h>
34 
35 #if (defined(__APPLE__) && defined(__MACH__)) || defined(__OpenBSD__) || defined(__FreeBSD__) || defined(__DragonFly__)
36 #include <sys/param.h>
37 #include <sys/sysctl.h>
38 #include <limits.h>
39 #include <fcntl.h>
40 #endif
41 #if defined(__FreeBSD__) || defined(__DragonFly__)
42 #include <kvm.h>
43 #include <sys/user.h>
44 #endif
45 
46 #if defined(__sun__)
47 #include <kstat.h>
48 #endif
49 
50 #if (defined(__APPLE__) && defined(__MACH__))
51 #include <mach/mach.h>
52 #endif
53 
54 #include <errno.h>
55 
56 #if defined(__sun__) || defined(__linux__)
57 #include <sys/sysinfo.h>
58 #endif
59 
60 #if defined(__linux__)
61 
62 #define PROCSTAT "/proc/stat"
63 #define BUFFERSIZE (256)
64 typedef struct {
65     unsigned int id;
66     unsigned long long
67 	/* total, */
68 	user,
69 	nice_user,
70 	kernel,
71 	idle,
72 	io_wait,
73 	hard_irq,
74 	soft_irq,
75 	steal;
76 } cpu_t;
77 
78 #endif
79 
80 #if (defined(__APPLE__) && defined(__MACH__))
81 #define CU_OSX_VALUES (5)
82 #endif
83 
84 #if defined(__FreeBSD__)
85 #include <sys/resource.h>
86 #include <sys/sysctl.h>
87 #define CU_BSD_VALUES (6)
88 #endif
89 
90 
91 #define FD_IN		(0)
92 #define FD_OUT		(1)
93 #define FD_ERR		(2)
94 
95 #define PING		'p'
96 #define NPROCS		'n'
97 #define AVG1		'1'
98 #define AVG5		'5'
99 #define AVG15		'f'
100 #define UTIL		'u'
101 #define QUIT		'q'
102 
103 
104 #define CU_CPU_ID	(0)
105 #define CU_USER		(1)
106 #define CU_NICE_USER	(2)
107 #define CU_KERNEL	(3)
108 #define CU_IO_WAIT	(4)
109 #define CU_IDLE		(5)
110 #define CU_HARD_IRQ	(6)
111 #define CU_SOFT_IRQ	(7)
112 #define CU_STEAL	(8)
113 
114 #define CU_VALUES	(9)
115 #define CU_KSTAT_VALUES	(5)
116 
117 /*
118 #define CU_FLG_CPU_ID		(0 << 1)
119 #define CU_FLG_USER		(0 << 2)
120 #define CU_FLG_NICE_USER	(0 << 3)
121 #define CU_FLG_KERNEL		(0 << 4)
122 #define CU_FLG_IO_WAIT		(0 << 5)
123 #define CU_FLG_IDLE		(0 << 6)
124 #define CU_FLG_HARD_IRQ		(0 << 7)
125 #define CU_FLG_SOFT_IRQ		(0 << 8)
126 #define CU_FLG_STEAL		(0 << 9)
127 */
128 
129 /* util_measure
130  * In:
131  * 	unsigned int **result_vec
132  * 	int *result_sz
133  * Purpose:
134  * 	Retrieve CPU utilization
135  * 	result_vec has 2 + np*ne*2 entries where np is number_of_cpus
136  * 	|------|------|
137  * 	|  np  |  ne  |
138  * 	|------|------|
139  * 	|val_id| value| (One entry)
140  * 	|------|------|
141  * 	|val_id| value| (One entry)
142  * 	|------|------|
143  *  	     ......
144  * 	|------|------|
145  * 	|val_id| value|
146  * 	|------|------|
147  * 	np = number of processors
148  * 	ne = number of entries per processor
149  */
150 
151 static void util_measure(unsigned int **result_vec, int *result_sz);
152 
153 #if defined(__sun__)
154 static unsigned int misc_measure(char* name);
155 #elif defined(__linux__)
156 static unsigned int misc_measure(char cmd);
157 #endif
158 static void sendi(unsigned int data);
159 static void sendv(unsigned int data[], int ints);
160 static void error(char* err_msg);
161 
162 #if (defined(__APPLE__) && defined(__MACH__)) || defined(__OpenBSD__) || defined(__FreeBSD__) || defined(__DragonFly__)
163 static void bsd_count_procs(void);
164 static void bsd_loadavg(int);
165 #endif
166 
167 #if defined(__sun__)
168 static kstat_ctl_t *kstat_ctl;
169 #endif
170 
171 #if defined(__linux__)
processors_online()172 static int processors_online() {
173     return (int)sysconf(_SC_NPROCESSORS_ONLN);
174 }
175 #endif
176 
177 #if (defined(__APPLE__) && defined(__MACH__)) || defined(__FreeBSD__)
178 void getsysctl(const char *, void *, size_t);
179 #endif
180 
main(int argc,char ** argv)181 int main(int argc, char** argv) {
182   char cmd;
183   int rc;
184   int sz;
185   unsigned int *rv;
186 #if defined(__linux__) || (defined(__APPLE__) && defined(__MACH__)) ||defined(__FreeBSD__)
187   unsigned int no_of_cpus = 0;
188 #endif
189 
190 #if defined(__sun__)
191   kstat_ctl = kstat_open();
192   if(!kstat_ctl)
193     error("Can't open header kstat");
194 #endif
195 
196 #if defined(__linux__)
197     no_of_cpus = processors_online();
198     if ( (rv = (unsigned int*)malloc(sizeof(unsigned int)*(2 + 2*no_of_cpus*CU_VALUES))) == NULL) {
199 	error("cpu_sup: malloc error");
200     }
201 #endif
202 
203 #if (defined(__APPLE__) && defined(__MACH__))
204     getsysctl("hw.ncpu", &no_of_cpus, sizeof(int));
205     if ( (rv = (unsigned int*)malloc(sizeof(unsigned int)*(2 + 2*no_of_cpus*CU_OSX_VALUES))) == NULL) {
206 	error("cpu_sup: malloc error");
207     }
208 #endif
209 
210 #if defined(__FreeBSD__)
211     getsysctl("hw.ncpu", &no_of_cpus, sizeof(int));
212     if ( (rv = (unsigned int*)malloc(sizeof(unsigned int)*(2 + 2*no_of_cpus*CU_BSD_VALUES))) == NULL) {
213 	error("cpu_sup: malloc error");
214     }
215 #endif
216 
217   while(1) {
218 
219     rc = read(FD_IN, &cmd, 1);
220     if (rc < 0) {
221       if (errno == EINTR)
222 	continue;
223       error("Error reading from Erlang");
224     }
225 
226     if(rc == 0)
227       error("Erlang has closed");
228 
229     switch(cmd) {
230     case PING:		sendi(4711);					break;
231 #if defined(__sun__)
232     case NPROCS:	sendi(misc_measure("nproc"));			break;
233     case AVG1:		sendi(misc_measure("avenrun_1min"));		break;
234     case AVG5:		sendi(misc_measure("avenrun_5min"));		break;
235     case AVG15:		sendi(misc_measure("avenrun_15min"));		break;
236 #elif defined(__linux__)
237     case NPROCS:
238     case AVG1:
239     case AVG5:
240     case AVG15:		sendi(misc_measure(cmd));			break;
241 #elif defined(__OpenBSD__) || (defined(__APPLE__) && defined(__MACH__)) || defined(__FreeBSD__) || defined(__DragonFly__)
242     case NPROCS:	bsd_count_procs();				break;
243     case AVG1:		bsd_loadavg(0);					break;
244     case AVG5:		bsd_loadavg(1);					break;
245     case AVG15:		bsd_loadavg(2);					break;
246 #endif
247 #if defined(__sun__) || defined(__linux__) || (defined(__APPLE__) && defined(__MACH__)) || defined(__FreeBSD__)
248     case UTIL:		util_measure(&rv,&sz); sendv(rv, sz);		break;
249 #endif
250     case QUIT:		free((void*)rv); return 0;
251     default:		error("Bad command");				break;
252     }
253   }
254   return 0; /* suppress warnings */
255 }
256 
257 /* ---------------------------- *
258  *     BSD stat functions 	*
259  * ---------------------------- */
260 #if defined(__OpenBSD__) || (defined(__APPLE__) && defined(__MACH__)) || defined(__FreeBSD__) || defined(__DragonFly__)
261 
bsd_loadavg(int idx)262 static void bsd_loadavg(int idx) {
263     double avgs[3];
264     if (getloadavg(avgs, 3) < 0) {
265 	error(strerror(errno));
266 	return;
267     }
268     sendi((unsigned int)(avgs[idx] * 256));
269 }
270 
271 #endif
272 
273 #if defined(__OpenBSD__)
274 
bsd_count_procs(void)275 static void bsd_count_procs(void) {
276     int err, nproc;
277     size_t len = sizeof(nproc);
278     int mib[] = { CTL_KERN, KERN_NPROCS };
279 
280     err = sysctl(mib, sizeof(mib) / sizeof(mib[0]), &nproc, &len, NULL, 0);
281     if (err) {
282 	error(strerror(errno));
283 	return;
284     }
285 
286     sendi((unsigned int)nproc);
287 }
288 
289 #elif defined(__FreeBSD__) || defined(__DragonFly__)
290 
bsd_count_procs(void)291 static void bsd_count_procs(void) {
292     kvm_t *kd;
293     struct kinfo_proc *kp;
294     char err[_POSIX2_LINE_MAX];
295     int cnt = 0;
296 
297     if ((kd = kvm_open(NULL, "/dev/null", NULL, O_RDONLY, err)) == NULL) {
298 	error(err);
299 	return;
300     }
301 
302 #if defined(KERN_PROC_PROC)
303     if ((kp = kvm_getprocs(kd, KERN_PROC_PROC, 0, &cnt)) == NULL) {
304 #else
305     if ((kp = kvm_getprocs(kd, KERN_PROC_ALL, 0, &cnt)) == NULL) {
306 #endif
307 	error(strerror(errno));
308 	return;
309     }
310 
311     (void)kvm_close(kd);
312     sendi((unsigned int)cnt);
313 }
314 
315 #elif (defined(__APPLE__) && defined(__MACH__))
316 
317 static void bsd_count_procs(void) {
318     int err;
319     size_t len = 0;
320     int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_ALL };
321 
322     err = sysctl(mib, sizeof(mib) / sizeof(mib[0]), NULL, &len, NULL, 0);
323     if (err) {
324 	error(strerror(errno));
325 	return;
326     }
327 
328     sendi((unsigned int)(len / sizeof(struct kinfo_proc)));
329 }
330 
331 #endif
332 
333 /* ---------------------------- *
334  *     Linux stat functions 	*
335  * ---------------------------- */
336 
337 #if defined(__linux__)
338 
339 static unsigned int misc_measure(char cmd) {
340     struct sysinfo info;
341 
342     if (sysinfo(&info))
343         error(strerror(errno));
344 
345     switch (cmd) {
346     case AVG1:	 	return (unsigned int)(info.loads[0] / 256);
347     case AVG5:	 	return (unsigned int)(info.loads[1] / 256);
348     case AVG15: 	return (unsigned int)(info.loads[2] / 256);
349     case NPROCS:	return info.procs;
350     }
351 
352     return -1;
353 }
354 
355 static cpu_t *read_procstat(FILE *fp, cpu_t *cpu) {
356     char buffer[BUFFERSIZE];
357 
358     if (fgets(buffer, BUFFERSIZE, fp) == NULL) {
359 	memset(cpu, 0, sizeof(cpu_t));
360 	return cpu;
361     }
362     sscanf(buffer, "cpu%u %llu %llu %llu %llu %llu %llu %llu %llu",
363 	&(cpu->id),
364 	&(cpu->user),
365 	&(cpu->nice_user),
366 	&(cpu->kernel),
367 	&(cpu->idle),
368 	&(cpu->io_wait),
369 	&(cpu->hard_irq),
370 	&(cpu->soft_irq),
371 	&(cpu->steal));
372 
373     return cpu;
374 }
375 
376 static void util_measure(unsigned int **result_vec, int *result_sz) {
377     int no_of_cpus = processors_online();
378     int i;
379     char buffer[BUFFERSIZE];
380     FILE *fp;
381     unsigned int *rv = NULL;
382     cpu_t cpu;
383 
384     rv = *result_vec;
385     rv[0] = no_of_cpus;
386 
387     if ( (fp = fopen(PROCSTAT,"r")) == NULL) {
388         if (errno == EACCES) { /* SELinux */
389             rv[1] = 1; /* just the cpu id */
390             ++rv; /* first value is number of cpus */
391             ++rv; /* second value is number of entries */
392             for (i = 0; i < no_of_cpus; ++i) {
393                 rv[0] = CU_CPU_ID;
394                 rv[1] = i;
395 	        rv += 1*2;
396             }
397             *result_sz = 2 + 2*1 * no_of_cpus;
398             return;
399         }
400 
401 	/* Check if procfs is mounted,
402 	 *  otherwise:
403 	 *  try and try again, bad procsfs.
404 	 */
405 	*result_sz = 0;
406 	return;
407     }
408 
409     /*ignore read*/
410     if (fgets(buffer, BUFFERSIZE, fp) == NULL) {
411 	*result_sz = 0;
412 	return;
413     }
414 
415     rv[1] = CU_VALUES;
416     ++rv; /* first value is number of cpus */
417     ++rv; /* second value is number of entries */
418 
419     for (i = 0; i < no_of_cpus; ++i) {
420 	read_procstat(fp, &cpu);
421 
422 	rv[ 0] = CU_CPU_ID;    rv[ 1] = cpu.id;
423 	rv[ 2] = CU_USER;      rv[ 3] = cpu.user;
424 	rv[ 4] = CU_NICE_USER; rv[ 5] = cpu.nice_user;
425 	rv[ 6] = CU_KERNEL;    rv[ 7] = cpu.kernel;
426 	rv[ 8] = CU_IO_WAIT;   rv[ 9] = cpu.io_wait;
427 	rv[10] = CU_IDLE;      rv[11] = cpu.idle;
428 	rv[12] = CU_HARD_IRQ;  rv[13] = cpu.hard_irq;
429 	rv[14] = CU_SOFT_IRQ;  rv[15] = cpu.soft_irq;
430 	rv[16] = CU_STEAL;     rv[17] = cpu.steal;
431 	rv += CU_VALUES*2;
432     }
433 
434     fclose(fp);
435     *result_sz = 2 + 2*CU_VALUES * no_of_cpus;
436 }
437 
438 #endif
439 
440 /* ---------------------------- *
441  *     Unix kstat functions 	*
442  * ---------------------------- */
443 
444 #if defined(__sun__)
445 static unsigned int misc_measure(char* name) {
446   static kstat_t *ksp = NULL;
447   kstat_named_t* entry;
448   kid_t kcid;
449 
450   kcid = kstat_chain_update(kstat_ctl);
451 
452   if(kcid == -1)
453     error("Error updating kstat chain");
454 
455   if (!ksp || kcid != 0) {
456 
457     /* The kstat chain changed (or we are initializing);
458        find system misc entry in the new chain... */
459 
460     ksp = kstat_lookup(kstat_ctl,"unix",0,"system_misc");
461     if(!ksp)
462       error("Can't open system_misc kstat");
463   }
464 
465   kstat_read(kstat_ctl,ksp,NULL);
466   entry = kstat_data_lookup(ksp,name);
467   if(!entry)
468     return -1;
469 
470   if(entry->data_type != KSTAT_DATA_UINT32)
471     return -1;
472 
473   return entry->value.ui32;
474 }
475 
476 
477 static int cpu_cmp(const void *p1, const void *p2) {
478   kstat_t *ksp1 = *((kstat_t **) p1);
479   kstat_t *ksp2 = *((kstat_t **) p2);
480 
481   if (ksp1->ks_instance > ksp2->ks_instance)
482     return 1;
483   if (ksp1->ks_instance < ksp2->ks_instance)
484     return -1;
485   return 0;
486 }
487 
488 
489 static void util_measure(unsigned int **result_vec, int *result_sz) {
490   static int no_of_cpus = 0;
491   static kstat_t **cpu_ksps = NULL;
492   static unsigned int * resv = NULL;
493   unsigned int *rv = NULL;
494   kstat_t *ksp;
495   kid_t kcid;
496   int cpu_stats_read;
497   int i;
498 
499   kcid = kstat_chain_update(kstat_ctl);
500 
501   if(kcid == -1)
502     error("Error updating kstat chain");
503 
504   if (no_of_cpus == 0 || kcid != 0) {
505 
506     /* The kstat chain changed (or we are initializing);
507        find cpu_stat entries in the new chain... */
508 
509     no_of_cpus = 0;
510 
511     for(ksp = kstat_ctl->kc_chain; ksp; ksp = ksp->ks_next) {
512       if (strcmp(ksp->ks_module, "cpu_stat") == 0
513 	  && ksp->ks_type == KSTAT_TYPE_RAW) {
514 	no_of_cpus++;
515 	/* Assumes that modifications of the cpu_stat_t struct
516 	   in future releases of Solaris only are additions
517 	   of fields at the end of the struct. */
518 	if(ksp->ks_data_size < sizeof(cpu_stat_t))
519 	  error("Error: unexpected kstat data size");
520       }
521     }
522 
523     free((void *) cpu_ksps);
524     if (no_of_cpus > 0) {
525       cpu_ksps = (kstat_t **) malloc(no_of_cpus*sizeof(kstat_t *));
526       if(!cpu_ksps)
527 	error("Error allocating memory");
528 
529       i = 0;
530       for(ksp = kstat_ctl->kc_chain;
531 	  ksp && i < no_of_cpus;
532 	  ksp = ksp->ks_next) {
533 	if (strcmp(ksp->ks_module, "cpu_stat") == 0
534 	    && ksp->ks_type == KSTAT_TYPE_RAW) {
535 	  cpu_ksps[i++] = ksp;
536 	}
537       }
538 
539       if (i != no_of_cpus)
540 	error("Error: private kstat chain copy unexpectedly changed");
541 
542       /* Erlang assumes that cpu information are sent in ascending order;
543 	 sort them ... */
544       qsort((void  *)cpu_ksps,(size_t)no_of_cpus,sizeof(kstat_t *),cpu_cmp);
545 
546     }
547 
548     free((void *) resv);
549 /* kstat defined values are:
550  *	CU_CPU_ID
551  *	CU_USER
552  *	CU_KERNEL
553  *	CU_IO_WAIT
554  *	CU_IDLE
555  */
556     resv = (unsigned int *) malloc(sizeof(unsigned int)*(2 + 2*no_of_cpus*CU_KSTAT_VALUES));
557     if(!resv)
558       error("Error allocating memory");
559 
560   }
561 
562     /* Read cpu utilization statistics ... */
563 
564     rv = resv;
565     rv++; /*first entry is np*/
566     rv++; /*second entry is ne*/
567     cpu_stats_read = 0;
568 
569     for(i = 0; i < no_of_cpus; i++) {
570 	if (kstat_read(kstat_ctl, cpu_ksps[i], NULL) != -1) {
571 	    cpu_stat_t *cpu_stat = (cpu_stat_t *)cpu_ksps[i]->ks_data;
572 
573 	    rv[ 0] = CU_CPU_ID;    rv[ 1] = cpu_ksps[i]->ks_instance;
574 	    rv[ 2] = CU_USER;      rv[ 3] = cpu_stat->cpu_sysinfo.cpu[CPU_USER];
575 	    rv[ 4] = CU_KERNEL;    rv[ 5] = cpu_stat->cpu_sysinfo.cpu[CPU_KERNEL];
576 	    rv[ 6] = CU_IO_WAIT;   rv[ 7] = cpu_stat->cpu_sysinfo.cpu[CPU_WAIT];
577 	    rv[ 8] = CU_IDLE;      rv[ 9] = cpu_stat->cpu_sysinfo.cpu[CPU_IDLE];
578 
579 	    rv += CU_KSTAT_VALUES*2;
580 	    cpu_stats_read++;
581  	}
582     }
583 
584     resv[0] = cpu_stats_read;
585     resv[1] = CU_KSTAT_VALUES;
586 
587     *result_vec = resv;
588     *result_sz = 2 + 2* CU_KSTAT_VALUES * cpu_stats_read;
589 
590 }
591 #endif
592 
593 /* ---------------------------- *
594  *     OSX util functions      *
595  * ---------------------------- */
596 
597 #if (defined(__APPLE__) && defined(__MACH__))
598 
599 static void util_measure(unsigned int **result_vec, int *result_sz) {
600     natural_t no_of_cpus;
601     processor_info_array_t info_array;
602     mach_msg_type_number_t info_count;
603     mach_port_t host_port;
604     kern_return_t error;
605     processor_cpu_load_info_data_t *cpu_load_info = NULL;
606     unsigned int *rv = NULL;
607     int i;
608 
609     host_port = mach_host_self();
610     error = host_processor_info(host_port, PROCESSOR_CPU_LOAD_INFO,
611                                 &no_of_cpus, &info_array, &info_count);
612     if (error != KERN_SUCCESS) {
613       *result_sz = 0;
614       return;
615     }
616     mach_port_deallocate(mach_task_self(), host_port);
617     cpu_load_info = (processor_cpu_load_info_data_t *) info_array;
618 
619     rv = *result_vec;
620     rv[0] = no_of_cpus;
621     rv[1] = CU_OSX_VALUES;
622     ++rv; /* first value is number of cpus */
623     ++rv; /* second value is number of entries */
624 
625     for (i = 0; i < no_of_cpus; ++i) {
626         rv[0] = CU_CPU_ID;    rv[1] = i;
627         rv[2] = CU_USER;      rv[3] = cpu_load_info[i].cpu_ticks[CPU_STATE_USER];
628         rv[4] = CU_NICE_USER; rv[5] = cpu_load_info[i].cpu_ticks[CPU_STATE_NICE];
629         rv[6] = CU_KERNEL;    rv[7] = cpu_load_info[i].cpu_ticks[CPU_STATE_SYSTEM];
630         rv[8] = CU_IDLE;      rv[9] = cpu_load_info[i].cpu_ticks[CPU_STATE_IDLE];
631         rv += CU_OSX_VALUES*2;
632     }
633 
634     *result_sz = 2 + 2*CU_OSX_VALUES * no_of_cpus;
635 
636     error = vm_deallocate(mach_task_self(), (vm_address_t)info_array,
637                         info_count * sizeof(int));
638     if (error != KERN_SUCCESS)
639       *result_sz = 0;
640 }
641 #endif
642 
643 /* ---------------------------- *
644  *  Utils for OSX and FreeBSD 	*
645  * ---------------------------- */
646 
647 #if (defined(__APPLE__) && defined(__MACH__)) || defined(__FreeBSD__)
648 
649 #define EXIT_WITH(msg) (rich_error(msg, __FILE__, __LINE__))
650 #define RICH_BUFLEN    (213)  /* left in error(char*) */
651 
652 void rich_error(const char *reason, const char *file, const int line) {
653     char buf[RICH_BUFLEN];
654     snprintf(buf, RICH_BUFLEN, "%s (%s:%i)", reason, file, line);
655     error(buf);
656 }
657 #undef RICH_BUFLEN
658 
659 void getsysctl(const char *name, void *ptr, size_t len)
660 {
661     size_t gotlen = len;
662     if (sysctlbyname(name, ptr, &gotlen, NULL, 0) != 0) {
663 	EXIT_WITH("sysctlbyname failed");
664     }
665     if (gotlen != len) {
666 	EXIT_WITH("sysctlbyname: unexpected length");
667     }
668 }
669 #endif
670 
671 
672 /* ---------------------------- *
673  *     FreeBSD stat functions 	*
674  * ---------------------------- */
675 
676 #if defined(__FreeBSD__)
677 
678 static void util_measure(unsigned int **result_vec, int *result_sz) {
679     int no_of_cpus;
680     size_t size_cpu_times;
681     unsigned long *cpu_times;
682     unsigned int *rv = NULL;
683     int i;
684 
685     getsysctl("hw.ncpu", &no_of_cpus, sizeof(int));
686     /* Header constant CPUSTATES = #long values per cpu. */
687     size_cpu_times = sizeof(long) * CPUSTATES * no_of_cpus;
688     cpu_times = malloc(size_cpu_times);
689     if (!cpu_times) {
690 	EXIT_WITH("badalloc");
691     }
692     getsysctl("kern.cp_times", cpu_times, size_cpu_times);
693 
694     rv = *result_vec;
695     rv[0] = no_of_cpus;
696     rv[1] = CU_BSD_VALUES;
697     ++rv; /* first value is number of cpus */
698     ++rv; /* second value is number of entries */
699 
700     for (i = 0; i < no_of_cpus; ++i) {
701         int offset = i * CPUSTATES;
702 	rv[ 0] = CU_CPU_ID;    rv[ 1] = i;
703 	rv[ 2] = CU_USER;      rv[ 3] = cpu_times[CP_USER + offset];
704 	rv[ 4] = CU_NICE_USER; rv[ 5] = cpu_times[CP_NICE + offset];
705 	rv[ 6] = CU_KERNEL;    rv[ 7] = cpu_times[CP_SYS + offset];
706 	rv[ 8] = CU_IDLE;      rv[ 9] = cpu_times[CP_IDLE + offset];
707 	rv[10] = CU_HARD_IRQ;  rv[11] = cpu_times[CP_INTR + offset];
708 	rv += CU_BSD_VALUES*2;
709     }
710 
711     *result_sz = 2 + 2*CU_BSD_VALUES * no_of_cpus;
712 }
713 #endif
714 
715 
716 /* ---------------------------- *
717  *	 Generic functions 	*
718  * ---------------------------- */
719 
720 static void sendi(unsigned int data) { sendv(&data, 1); }
721 
722 static void sendv(unsigned int data[], int ints) {
723     static unsigned char *buf = NULL;
724     static int bufsz = 0;
725     int rc, di, bi, msgsz;
726 
727     /* Assumes 32-bit integers... */
728 
729     msgsz = 4*ints;
730 
731     if(bufsz < msgsz) {
732 	if (buf != NULL) free((void *) buf);
733 	buf = malloc(msgsz);
734 	if (!buf) error("Error allocating memory");
735 	bufsz = msgsz;
736     }
737 
738     for(bi = 0, di = 0; di < ints; di++) {
739 	buf[bi++] = (data[di] >> 24) & 0xff;
740 	buf[bi++] = (data[di] >> 16) & 0xff;
741 	buf[bi++] = (data[di] >>  8) & 0xff;
742 	buf[bi++] = (data[di]      ) & 0xff;
743     }
744 
745   bi = 0;
746   do {
747     rc = write(FD_OUT, &buf[bi], msgsz - bi);
748     if (rc < 0) {
749       if (errno == EINTR)
750 	continue;
751       error("Error writing to Erlang");
752     }
753     bi += rc;
754   } while(msgsz - bi > 0);
755 
756 }
757 
758 static void error(char* err_msg) {
759   /*
760    * if we get error here we have trouble,
761    * silence unnecessary warnings
762    */
763   char buffer[256] = "[os_mon] cpu supervisor port (cpu_sup): ";
764   int i = strlen(buffer), j = 0;
765   int n = strlen(err_msg);
766 
767   while(i < 253 && j < n) {
768       buffer[i++] = err_msg[j++];
769   }
770   buffer[i++] = '\r';
771   buffer[i++] = '\n';
772 
773   /* try to use one write only */
774   if(write(FD_ERR, buffer, i))
775      ;
776   exit(-1);
777 }
778