xref: /6.6.0/sigar/src/sigar_util.c (revision 829a4c03)
1/*
2 * Copyright (c) 2004-2009 Hyperic, Inc.
3 * Copyright (c) 2009 SpringSource, Inc.
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 *     http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18#include <stdio.h>
19#include <stdlib.h>
20#include <errno.h>
21#include <fcntl.h>
22#include <assert.h>
23
24#include "sigar.h"
25#include "sigar_private.h"
26#include "sigar_util.h"
27#include "sigar_os.h"
28
29#ifndef WIN32
30
31#include <dirent.h>
32#include <sys/stat.h>
33
34char *sigar_uitoa(char *buf, unsigned int n, int *len)
35{
36    char *start = buf + UITOA_BUFFER_SIZE - 1;
37
38    *start = 0;
39
40    do {
41        *--start = '0' + (n % 10);
42        ++*len;
43        n /= 10;
44    } while (n);
45
46    return start;
47}
48
49char *sigar_skip_line(char *buffer, int buflen)
50{
51    char *ptr = buflen ?
52        (char *)memchr(buffer, '\n', buflen) : /* bleh */
53        strchr(buffer, '\n');
54    return ++ptr;
55}
56
57char *sigar_skip_token(char *p)
58{
59    while (sigar_isspace(*p)) p++;
60    while (*p && !sigar_isspace(*p)) p++;
61    return p;
62}
63
64char *sigar_skip_multiple_token(char *p, int count)
65{
66    int i;
67
68    for (i = 0; i < count; i++) {
69        p = sigar_skip_token(p);
70    }
71
72    return p;
73}
74
75char *sigar_getword(char **line, char stop)
76{
77    char *pos = *line;
78    int len;
79    char *res;
80
81    while ((*pos != stop) && *pos) {
82        ++pos;
83    }
84
85    len = pos - *line;
86    res = malloc(len + 1);
87    memcpy(res, *line, len);
88    res[len] = 0;
89
90    if (stop) {
91        while (*pos == stop) {
92            ++pos;
93        }
94    }
95
96    *line = pos;
97
98    return res;
99}
100
101/* avoiding sprintf */
102
103char *sigar_proc_filename(char *buffer, int buflen,
104                          sigar_pid_t bigpid,
105                          const char *fname, int fname_len)
106{
107    int len = 0;
108    char *ptr = buffer;
109    unsigned int pid = (unsigned int)bigpid; /* XXX -- This isn't correct */
110    char pid_buf[UITOA_BUFFER_SIZE];
111    char *pid_str = sigar_uitoa(pid_buf, pid, &len);
112
113    assert((unsigned int)buflen >=
114           (SSTRLEN(PROCP_FS_ROOT) + UITOA_BUFFER_SIZE + fname_len + 1));
115
116    memcpy(ptr, PROCP_FS_ROOT, SSTRLEN(PROCP_FS_ROOT));
117    ptr += SSTRLEN(PROCP_FS_ROOT);
118
119    memcpy(ptr, pid_str, len);
120    ptr += len;
121
122    memcpy(ptr, fname, fname_len);
123    ptr += fname_len;
124    *ptr = '\0';
125
126    return buffer;
127}
128
129int sigar_proc_file2str(char *buffer, int buflen,
130                        sigar_pid_t pid,
131                        const char *fname,
132                        int fname_len)
133{
134    int retval;
135
136    buffer = sigar_proc_filename(buffer, buflen, pid,
137                                 fname, fname_len);
138
139    retval = sigar_file2str(buffer, buffer, buflen);
140
141    if (retval != SIGAR_OK) {
142        switch (retval) {
143          case ENOENT:
144            retval = ESRCH; /* no such process */
145          default:
146            break;
147        }
148    }
149
150    return retval;
151}
152
153int sigar_proc_list_procfs_get(sigar_t *sigar,
154                               sigar_proc_list_t *proclist)
155{
156    DIR *dirp = opendir("/proc");
157    struct dirent *ent;
158#ifdef HAVE_READDIR_R
159    struct dirent dbuf;
160#endif
161
162    if (!dirp) {
163        return errno;
164    }
165
166#ifdef HAVE_READDIR_R
167    while (readdir_r(dirp, &dbuf, &ent) == 0) {
168        if (ent == NULL) {
169            break;
170        }
171#else
172    while ((ent = readdir(dirp))) {
173#endif
174        if (!sigar_isdigit(*ent->d_name)) {
175            continue;
176        }
177
178        /* XXX: more sanity checking */
179
180        SIGAR_PROC_LIST_GROW(proclist);
181
182        proclist->data[proclist->number++] =
183            strtoul(ent->d_name, NULL, 10);
184    }
185
186    closedir(dirp);
187
188    return SIGAR_OK;
189}
190
191int sigar_proc_fd_count(sigar_t *sigar, sigar_pid_t pid,
192                        sigar_uint64_t *total)
193{
194    DIR *dirp;
195    struct dirent *ent;
196#ifdef HAVE_READDIR_R
197    struct dirent dbuf;
198#endif
199    char name[BUFSIZ];
200
201    (void)SIGAR_PROC_FILENAME(name, pid, "/fd");
202
203    *total = 0;
204
205    if (!(dirp = opendir(name))) {
206        return errno;
207    }
208
209#ifdef HAVE_READDIR_R
210    while (readdir_r(dirp, &dbuf, &ent) == 0) {
211        if (ent == NULL) {
212            break;
213        }
214#else
215    while ((ent = readdir(dirp))) {
216#endif
217        if (!sigar_isdigit(*ent->d_name)) {
218            continue;
219        }
220
221        (*total)++;
222    }
223
224    closedir(dirp);
225
226    return SIGAR_OK;
227}
228
229
230#endif /* WIN32 */
231
232int sigar_mem_calc_ram(sigar_t *sigar, sigar_mem_t *mem)
233{
234    sigar_int64_t total = mem->total / 1024, diff;
235    sigar_uint64_t lram = (mem->total / (1024 * 1024));
236    int ram = (int)lram; /* must cast after division */
237    int remainder = ram % 8;
238
239    if (remainder > 0) {
240        ram += (8 - remainder);
241    }
242
243    mem->ram = ram;
244
245    diff = total - (mem->actual_free / 1024);
246    mem->used_percent =
247        (double)(diff * 100) / total;
248
249    diff = total - (mem->actual_used / 1024);
250    mem->free_percent =
251        (double)(diff * 100) / total;
252
253    return ram;
254}
255
256#ifndef WIN32
257
258sigar_iodev_t *sigar_iodev_get(sigar_t *sigar,
259                               const char *dirname)
260{
261    sigar_cache_entry_t *entry;
262    struct stat sb;
263    sigar_uint64_t id;
264    sigar_file_system_list_t fslist;
265    int i, status, is_dev=0;
266    int debug = SIGAR_LOG_IS_DEBUG(sigar);
267    char dev_name[SIGAR_FS_NAME_LEN];
268
269    if (!sigar->fsdev) {
270        sigar->fsdev = sigar_cache_new(15);
271    }
272
273    if (*dirname != '/') {
274        snprintf(dev_name, sizeof(dev_name),
275                 SIGAR_DEV_PREFIX "%s", dirname);
276        dirname = dev_name;
277        is_dev = 1;
278    }
279    else if (SIGAR_NAME_IS_DEV(dirname)) {
280        is_dev = 1;
281    }
282
283    if (stat(dirname, &sb) < 0) {
284        if (debug) {
285            sigar_log_printf(sigar, SIGAR_LOG_DEBUG,
286                             "[iodev] stat(%s) failed",
287                             dirname);
288        }
289        return NULL;
290    }
291
292    id = SIGAR_FSDEV_ID(sb);
293
294    entry = sigar_cache_get(sigar->fsdev, id);
295
296    if (entry->value != NULL) {
297        return (sigar_iodev_t *)entry->value;
298    }
299
300    if (is_dev) {
301        sigar_iodev_t *iodev;
302        entry->value = iodev = malloc(sizeof(*iodev));
303        SIGAR_ZERO(iodev);
304        SIGAR_SSTRCPY(iodev->name, dirname);
305        if (debug) {
306            sigar_log_printf(sigar, SIGAR_LOG_DEBUG,
307                             "[iodev] %s is_dev=true", dirname);
308        }
309        return iodev;
310    }
311
312    status = sigar_file_system_list_get(sigar, &fslist);
313
314    if (status != SIGAR_OK) {
315        sigar_log_printf(sigar, SIGAR_LOG_DEBUG,
316                         "[iodev] file_system_list failed: %s",
317                         sigar_strerror(sigar, status));
318        return NULL;
319    }
320
321    for (i=0; i<fslist.number; i++) {
322        sigar_file_system_t *fsp = &fslist.data[i];
323
324        if (fsp->type == SIGAR_FSTYPE_LOCAL_DISK) {
325            int retval = stat(fsp->dir_name, &sb);
326            sigar_cache_entry_t *ent;
327
328            if (retval < 0) {
329                if (debug) {
330                    sigar_log_printf(sigar, SIGAR_LOG_DEBUG,
331                                     "[iodev] inode stat(%s) failed",
332                                     fsp->dir_name);
333                }
334                continue; /* cant cache w/o inode */
335            }
336
337            ent = sigar_cache_get(sigar->fsdev, SIGAR_FSDEV_ID(sb));
338            if (ent->value) {
339                continue; /* already cached */
340            }
341
342            if (SIGAR_NAME_IS_DEV(fsp->dev_name)) {
343                sigar_iodev_t *iodev;
344                ent->value = iodev = malloc(sizeof(*iodev));
345                SIGAR_ZERO(iodev);
346                iodev->is_partition = 1;
347                SIGAR_SSTRCPY(iodev->name, fsp->dev_name);
348
349                if (debug) {
350                    sigar_log_printf(sigar, SIGAR_LOG_DEBUG,
351                                     "[iodev] map %s -> %s",
352                                     fsp->dir_name, iodev->name);
353                }
354            }
355        }
356    }
357
358    sigar_file_system_list_destroy(sigar, &fslist);
359
360    if (entry->value &&
361        (((sigar_iodev_t *)entry->value)->name[0] != '\0'))
362    {
363        return (sigar_iodev_t *)entry->value;
364    }
365    else {
366        return NULL;
367    }
368}
369#endif
370
371double sigar_file_system_usage_calc_used(sigar_t *sigar,
372                                         sigar_file_system_usage_t *fsusage)
373{
374    /*
375     * win32 will not convert __uint64 to double.
376     * convert to KB then do unsigned long -> double.
377     */
378    sigar_uint64_t b_used = (fsusage->total - fsusage->free) / 1024;
379    sigar_uint64_t b_avail = fsusage->avail / 1024;
380    unsigned long utotal = b_used + b_avail;
381    unsigned long used = b_used;
382
383    if (utotal != 0) {
384        unsigned long u100 = used * 100;
385        double pct = u100 / utotal +
386            ((u100 % utotal != 0) ? 1 : 0);
387        return pct / 100;
388    }
389
390    return 0;
391}
392
393typedef struct {
394    sigar_uint32_t eax;
395    sigar_uint32_t ebx;
396    sigar_uint32_t ecx;
397    sigar_uint32_t edx;
398} sigar_cpuid_t;
399
400#if defined(__GNUC__) && !defined(__sun)
401
402#  if defined(__i386__)
403#  define SIGAR_HAS_CPUID
404static void sigar_cpuid(sigar_uint32_t request, sigar_cpuid_t *id)
405{
406    /* derived from: */
407    /* http://svn.red-bean.com/repos/minor/trunk/gc/barriers-ia-32.c */
408    asm volatile ("mov %%ebx, %%esi\n\t"
409                  "cpuid\n\t"
410                  "xchgl %%ebx, %%esi"
411                  : "=a" (id->eax),
412                    "=S" (id->ebx),
413                    "=c" (id->ecx),
414                    "=d" (id->edx)
415                  : "0" (request)
416                  : "memory");
417}
418#  elif defined(__amd64__)
419#  define SIGAR_HAS_CPUID
420static void sigar_cpuid(sigar_uint32_t request,
421                        sigar_cpuid_t *id)
422{
423    /* http://svn.red-bean.com/repos/minor/trunk/gc/barriers-amd64.c */
424    asm volatile ("cpuid\n\t"
425                  : "=a" (id->eax),
426                    "=b" (id->ebx),
427                    "=c" (id->ecx),
428                    "=d" (id->edx)
429                  : "0" (request)
430                  : "memory");
431}
432#  endif
433#elif defined(WIN32)
434#  ifdef _M_X64
435#  include <intrin.h>
436#  define SIGAR_HAS_CPUID
437static void sigar_cpuid(sigar_uint32_t request,
438                        sigar_cpuid_t *id)
439{
440    sigar_uint32_t info[4];
441    __cpuid(info, request); /* as of MSVC 7 */
442    memcpy(id, &info[0], sizeof(info));
443}
444#  else
445#  define SIGAR_HAS_CPUID
446static void sigar_cpuid(sigar_uint32_t request,
447                        sigar_cpuid_t *id)
448{
449    __asm {
450        mov edi, id
451        mov eax, [edi].eax
452        mov ecx, [edi].ecx
453        cpuid
454        mov [edi].eax, eax
455        mov [edi].ebx, ebx
456        mov [edi].ecx, ecx
457        mov [edi].edx, edx
458    }
459}
460#  endif
461#endif
462
463#define INTEL_ID 0x756e6547
464#define AMD_ID   0x68747541
465
466int sigar_cpu_core_count(sigar_t *sigar)
467{
468#if defined(SIGAR_HAS_CPUID)
469    sigar_cpuid_t id;
470
471    if (sigar->lcpu == -1) {
472        sigar->lcpu = 1;
473
474        sigar_cpuid(0, &id);
475
476        if ((id.ebx == INTEL_ID) || (id.ebx == AMD_ID)) {
477            sigar_cpuid(1, &id);
478
479            if (id.edx & (1<<28)) {
480                sigar->lcpu = (id.ebx & 0x00FF0000) >> 16;
481            }
482        }
483
484        sigar_log_printf(sigar, SIGAR_LOG_DEBUG,
485                         "[cpu] %d cores per socket", sigar->lcpu);
486    }
487
488    return sigar->lcpu;
489#elif defined(__sun) || defined(__hpux) || defined(_AIX)
490    return 1;
491#else
492    sigar->lcpu = 1;
493    return sigar->lcpu;
494#endif
495}
496
497int sigar_cpu_core_rollup(sigar_t *sigar)
498{
499#ifdef SIGAR_HAS_CPUID
500    int log_rollup =
501        SIGAR_LOG_IS_DEBUG(sigar) &&
502        (sigar->lcpu == -1);
503
504    (void)sigar_cpu_core_count(sigar);
505
506    if (sigar->cpu_list_cores) {
507        if (log_rollup && (sigar->lcpu > 1)) {
508            sigar_log_printf(sigar, SIGAR_LOG_DEBUG,
509                             "[cpu] treating cores as-is");
510        }
511    }
512    else {
513        if (log_rollup && (sigar->lcpu > 1)) {
514            sigar_log_printf(sigar, SIGAR_LOG_DEBUG,
515                             "[cpu] rolling up cores to sockets");
516            return 1;
517        }
518    }
519#endif
520    return 0;
521}
522
523#define IS_CPU_R(p) \
524   ((*p == '(') && (*(p+1) == 'R') && (*(p+2) == ')'))
525
526typedef struct {
527    char *name;  /* search */
528    int len;
529    char *rname; /* replace */
530    int rlen;
531} cpu_model_str_t;
532
533/* to later replace 's' with 'r' */
534#define CPU_MODEL_ENT_R(s, r) \
535    { s, sizeof(s)-1, r, sizeof(r) }
536
537#define CPU_MODEL_ENT(s) \
538    CPU_MODEL_ENT_R(s, s)
539
540/* after the vendor part of the string is removed,
541 * looking for startsWith the entries below
542 * to remove the crap after the model name, see
543 * ../exp/intel_amd_cpu_models.txt
544 */
545static const cpu_model_str_t cpu_models[] = {
546    /* intel */
547    CPU_MODEL_ENT("Xeon"),
548    CPU_MODEL_ENT_R("XEON", "Xeon"),
549    CPU_MODEL_ENT("Pentium III"),
550    CPU_MODEL_ENT("Pentium II"),
551    CPU_MODEL_ENT_R("Pentium(R) III", "Pentium III"),
552    CPU_MODEL_ENT_R("Pentium(R) 4", "Pentium 4"),
553    CPU_MODEL_ENT_R("Pentium(R) M", "Pentium M"),
554    CPU_MODEL_ENT("Pentium Pro"),
555    CPU_MODEL_ENT("Celeron"),
556
557    /* amd */
558    CPU_MODEL_ENT("Opteron"),
559    CPU_MODEL_ENT("Athlon"),
560    CPU_MODEL_ENT("Duron"),
561    CPU_MODEL_ENT_R("K6(tm)-III", "K6 III"),
562    CPU_MODEL_ENT_R("K6(tm) 3D+", "K6 3D+"),
563    { NULL }
564};
565
566/* common to win32 and linux */
567void sigar_cpu_model_adjust(sigar_t *sigar, sigar_cpu_info_t *info)
568{
569    int len, i;
570    char model[128], *ptr=model, *end;
571
572    memcpy(model, info->model, sizeof(model));
573
574    /* trim leading and trailing spaces */
575    len = strlen(model);
576    end = &model[len-1];
577    while (*ptr == ' ') ++ptr;
578    while (*end == ' ') *end-- = '\0';
579
580    /* remove vendor from model name */
581    len = strlen(info->vendor);
582    if (strnEQ(ptr, info->vendor, len)) {
583        ptr += len;
584        if (IS_CPU_R(ptr)) {
585            ptr += 3; /* remove (R) */
586        }
587        while (*ptr == ' ') ++ptr;
588    }
589
590    if (*ptr == '-') {
591        ++ptr; /* e.g. was AMD-K6... */
592    }
593
594    for (i=0; cpu_models[i].name; i++) {
595        const cpu_model_str_t *cpu_model = &cpu_models[i];
596
597        if (strnEQ(ptr, cpu_model->name, cpu_model->len)) {
598            memcpy(info->model, cpu_model->rname, cpu_model->rlen);
599            return;
600        }
601    }
602
603    strcpy(info->model, ptr);
604}
605
606/* attempt to derive MHz from model name
607 * currently works for certain intel strings
608 * see exp/intel_amd_cpu_models.txt
609 */
610int sigar_cpu_mhz_from_model(char *model)
611{
612    int mhz = SIGAR_FIELD_NOTIMPL;
613    char *ptr = model;
614
615    while (*ptr && (ptr = strchr(ptr, ' '))) {
616        while(*ptr && !sigar_isdigit(*ptr)) {
617            ptr++;
618        }
619        mhz = sigar_strtoul(ptr);
620
621        if (*ptr == '.') {
622            /* e.g. "2.40GHz" */
623            ++ptr;
624            mhz *= 100;
625            mhz += sigar_strtoul(ptr);
626            break;
627        }
628        else if (strnEQ(ptr, "GHz", 3) ||
629                 strnEQ(ptr, "MHz", 3))
630        {
631            /* e.g. "1500MHz" */
632            break;
633        }
634        else {
635            mhz = SIGAR_FIELD_NOTIMPL;
636        }
637    }
638
639    if (mhz != SIGAR_FIELD_NOTIMPL) {
640        if (strnEQ(ptr, "GHz", 3)) {
641            mhz *= 10;
642        }
643    }
644
645    return mhz;
646}
647
648#if !defined(WIN32) && !defined(NETWARE)
649#include <netdb.h>
650#include <rpc/rpc.h>
651#include <rpc/pmap_prot.h>
652#include <rpc/pmap_clnt.h>
653#ifdef SIGAR_HPUX
654#include <nfs/nfs.h>
655#endif
656#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__sun) || defined(DARWIN)
657#include <arpa/inet.h>
658#endif
659#if defined(__sun) || defined(SIGAR_HPUX)
660#include <rpc/clnt_soc.h>
661#endif
662#if defined(_AIX) || defined(SIGAR_HPUX) || defined(__OpenBSD__) || defined(__NetBSD__)
663#include <sys/socket.h>
664#endif
665
666static enum clnt_stat get_sockaddr(struct sockaddr_in *addr, char *host)
667{
668    register struct hostent *hp;
669    sigar_hostent_t data;
670
671    memset(addr, 0, sizeof(struct sockaddr_in));
672    addr->sin_family = AF_INET;
673
674    if ((addr->sin_addr.s_addr = inet_addr(host)) == -1) {
675        if (!(hp = sigar_gethostbyname(host, &data))) {
676            return RPC_UNKNOWNHOST;
677        }
678        memcpy(&addr->sin_addr, hp->h_addr, hp->h_length);
679    }
680
681    return RPC_SUCCESS;
682}
683
684char *sigar_rpc_strerror(int err)
685{
686    return (char *)clnt_sperrno(err);
687}
688
689SIGAR_DECLARE(int) sigar_rpc_ping(char *host,
690                                  int protocol,
691                                  unsigned long program,
692                                  unsigned long version)
693{
694    CLIENT *client;
695    struct sockaddr_in addr;
696    int sock;
697    struct timeval timeout;
698    unsigned short port = 0;
699    enum clnt_stat rpc_stat;
700
701    rpc_stat = get_sockaddr(&addr, host);
702    if (rpc_stat != RPC_SUCCESS) {
703        return rpc_stat;
704    }
705
706    timeout.tv_sec = 2;
707    timeout.tv_usec = 0;
708    addr.sin_port = htons(port);
709    sock = RPC_ANYSOCK;
710
711    if (protocol == SIGAR_NETCONN_UDP) {
712        client =
713            clntudp_create(&addr, program, version,
714                           timeout, &sock);
715    }
716    else if (protocol == SIGAR_NETCONN_TCP) {
717        client =
718            clnttcp_create(&addr, program, version,
719                           &sock, 0, 0);
720    }
721    else {
722        return RPC_UNKNOWNPROTO;
723    }
724
725    if (!client) {
726        return rpc_createerr.cf_stat;
727    }
728
729    timeout.tv_sec = 10;
730    timeout.tv_usec = 0;
731    rpc_stat = clnt_call(client, NULLPROC, (xdrproc_t)xdr_void, NULL,
732                         (xdrproc_t)xdr_void, NULL, timeout);
733
734    clnt_destroy(client);
735
736    return rpc_stat;
737}
738#endif
739
740int sigar_file2str(const char *fname, char *buffer, int buflen)
741{
742    int len, status;
743    int fd = open(fname, O_RDONLY);
744
745    if (fd < 0) {
746        return ENOENT;
747    }
748
749    if ((len = read(fd, buffer, buflen)) < 0) {
750        status = errno;
751    }
752    else {
753        status = SIGAR_OK;
754        buffer[len] = '\0';
755    }
756    close(fd);
757
758    return status;
759}
760
761#ifdef WIN32
762#define vsnprintf _vsnprintf
763#endif
764
765#ifdef WIN32
766#   define rindex strrchr
767#endif
768
769#ifdef SIGAR_HAS_DLINFO_MODULES
770
771static int sigar_dlinfo_get(sigar_t *sigar, const char *func,
772                            void **handle, Link_map **map)
773{
774    Dl_info dli;
775
776    if (!dladdr((void *)((uintptr_t)sigar_dlinfo_get), &dli)) {
777        sigar_log_printf(sigar, SIGAR_LOG_ERROR,
778                         "[%s] dladdr(%s) = %s",
779                         func, SIGAR_FUNC, dlerror());
780        return ESRCH;
781    }
782
783    if (!(*handle = dlopen(dli.dli_fname, RTLD_LAZY))) {
784        sigar_log_printf(sigar, SIGAR_LOG_ERROR,
785                         "[%s] dlopen(%s) = %s",
786                         func, dli.dli_fname, dlerror());
787        return ESRCH;
788    }
789
790    dlinfo(*handle, RTLD_DI_LINKMAP, map);
791
792    if (!map) {
793        sigar_log_printf(sigar, SIGAR_LOG_ERROR,
794                         "[%s] dlinfo = %s",
795                         func, dlerror());
796        return ESRCH;
797    }
798
799    return SIGAR_OK;
800}
801
802int sigar_dlinfo_modules(sigar_t *sigar, sigar_proc_modules_t *procmods)
803{
804    int status;
805    void *handle;
806    Link_map *map;
807
808    status = sigar_dlinfo_get(sigar, SIGAR_FUNC, &handle, &map);
809    if (status != SIGAR_OK) {
810        return status;
811    }
812
813    while (map->l_prev != NULL) {
814        map = map->l_prev;
815    }
816
817    do {
818        int status =
819            procmods->module_getter(procmods->data,
820                                    (char *)map->l_name,
821                                    strlen(map->l_name));
822
823        if (status != SIGAR_OK) {
824            /* not an error; just stop iterating */
825            break;
826        }
827    } while ((map = map->l_next));
828
829    dlclose(handle);
830
831    return SIGAR_OK;
832}
833#endif
834
835SIGAR_DECLARE(void) sigar_log_printf(sigar_t *sigar, int level,
836                                     const char *format, ...)
837{
838    va_list args;
839    char buffer[8192];
840
841    if (level > sigar->log_level) {
842        return;
843    }
844
845    if (!sigar->log_impl) {
846        return;
847    }
848
849    va_start(args, format);
850    vsnprintf(buffer, sizeof(buffer), format, args);
851    va_end(args);
852
853    sigar->log_impl(sigar, sigar->log_data, level, buffer);
854}
855
856SIGAR_DECLARE(void) sigar_log(sigar_t *sigar, int level, char *message)
857{
858    if (level > sigar->log_level) {
859        return;
860    }
861
862    if (!sigar->log_impl) {
863        return;
864    }
865
866    sigar->log_impl(sigar, sigar->log_data, level, message);
867}
868
869SIGAR_DECLARE(void) sigar_log_impl_set(sigar_t *sigar, void *data,
870                                       sigar_log_impl_t impl)
871{
872    sigar->log_data = data;
873    sigar->log_impl = impl;
874}
875
876SIGAR_DECLARE(int) sigar_log_level_get(sigar_t *sigar)
877{
878    return sigar->log_level;
879}
880
881static const char *log_levels[] = {
882    "FATAL",
883    "ERROR",
884    "WARN",
885    "INFO",
886    "DEBUG",
887    "TRACE"
888};
889
890SIGAR_DECLARE(const char *) sigar_log_level_string_get(sigar_t *sigar)
891{
892    return log_levels[sigar->log_level];
893}
894
895SIGAR_DECLARE(void) sigar_log_level_set(sigar_t *sigar, int level)
896{
897    sigar->log_level = level;
898}
899
900SIGAR_DECLARE(void) sigar_log_impl_file(sigar_t *sigar, void *data,
901                                        int level, char *message)
902{
903    FILE *fp = (FILE*)data;
904    fprintf(fp, "[%s] %s\n", log_levels[level], message);
905}
906
907#ifndef WIN32
908sigar_int64_t sigar_time_now_millis(void)
909{
910    struct timeval tv;
911    gettimeofday(&tv, NULL);
912    return ((tv.tv_sec * SIGAR_USEC) + tv.tv_usec) / SIGAR_MSEC;
913}
914#endif
915