Skip to content
This repository
Browse code

Merge pull request #33 from nettag/master

disk io per process, and a few bug fixes
  • Loading branch information...
commit 189843858ecd8da7fb94a6b274269877c2416b3a 2 parents 907fe7a + 47f01e0
nira11 authored October 06, 2013
47  bindings/SigarWrapper.pm
@@ -43,6 +43,7 @@ my %has_name_arg = map { $_, 1 } qw(FileSystemUsage DiskUsage
43 43
                                     FileAttrs DirStat DirUsage
44 44
                                     NetInterfaceConfig NetInterfaceStat);
45 45
 
  46
+
46 47
 my %proc_no_arg = map { $_, 1 } qw(stat);
47 48
 
48 49
 my %get_not_impl = map { $_, 1 } qw(net_address net_route net_connection net_stat cpu_perc
@@ -527,6 +528,7 @@ use vars qw(%classes %cmds);
527 528
          plat => '*'
528 529
       },
529 530
     ],
  531
+
530 532
     ProcMem => [
531 533
       {
532 534
          name => 'size', type => 'Long',
@@ -627,6 +629,51 @@ use vars qw(%classes %cmds);
627 629
          plat => '*'
628 630
       },
629 631
     ],
  632
+    ProcDiskIO => [
  633
+      {
  634
+         name => 'bytes_read', type => 'Long',
  635
+         desc => 'Bytes Read',
  636
+         plat => 'LW'
  637
+      },
  638
+      {
  639
+         name => 'bytes_written', type => 'Long',
  640
+         desc => 'Bytes Written',
  641
+         plat => 'LW'
  642
+      },
  643
+      {
  644
+         name => 'bytes_total', type => 'Long',
  645
+         desc => 'Bytes Total',
  646
+         plat => 'LWAHS'
  647
+      }
  648
+    ],
  649
+
  650
+    ProcCumulativeDiskIO => [
  651
+      {
  652
+         name => 'bytes_read', type => 'Long',
  653
+         desc => 'Bytes Read from Start',
  654
+         plat => 'LW'
  655
+      },
  656
+      {
  657
+         name => 'bytes_written', type => 'Long',
  658
+         desc => 'Bytes Written from Start',
  659
+         plat => 'LW'
  660
+      },
  661
+      {
  662
+         name => 'bytes_total', type => 'Long',
  663
+         desc => 'Bytes Total from Start',
  664
+         plat => 'LWAHS'
  665
+      }
  666
+    ],
  667
+
  668
+    DumpPidCache => [
  669
+      {
  670
+         name => 'dummy', type => 'Long',
  671
+         desc => 'Dummy',
  672
+         plat => 'LWAHS'
  673
+      }
  674
+    ],
  675
+
  676
+
630 677
     ProcState => [
631 678
       {
632 679
          name => 'state', type => 'Char',
12  bindings/java/hyperic_jni/src/org/hyperic/jni/ArchLoader.java
@@ -17,6 +17,7 @@
17 17
 package org.hyperic.jni;
18 18
 
19 19
 import java.io.File;
  20
+import java.io.UnsupportedEncodingException;
20 21
 import java.util.StringTokenizer;
21 22
 import java.net.URL;
22 23
 import java.net.URLClassLoader;
@@ -315,7 +316,16 @@ private String findJarPath(String libName, boolean isRequired)
315 316
         if ((file != null) &&
316 317
             ((file = file.getParentFile()) != null))
317 318
         {
318  
-            String dir = URLDecoder.decode(file.toString()); 
  319
+            String dir;
  320
+			try {
  321
+				// Passing UTF-8 according to the recommendation in the URLDecoder.decode JavaDoc.
  322
+				dir = URLDecoder.decode(file.toString(), "UTF-8");
  323
+			} catch (UnsupportedEncodingException e) {
  324
+				String msg = "Unsupported encoding in file name: " + file.toString();
  325
+				ArchLoaderException archLoaderException = new ArchLoaderException(msg);
  326
+				archLoaderException.initCause(e);
  327
+				throw archLoaderException;
  328
+			}
319 329
             if (findNativeLibrary(dir, libName)) {
320 330
                 return dir;
321 331
             }
165  bindings/java/src/org/hyperic/sigar/FileInfo.java
@@ -292,74 +292,58 @@ public String diff(DirStat stat) {
292 292
     }
293 293
 
294 294
     public String diff(FileInfo info) {
295  
-        ArrayList changes = new ArrayList();
296  
-
297  
-        if (this.getMtime() != info.getMtime()) {
298  
-            changes.add(new Diff("Mtime",
299  
-                                 formatDate(info.getMtime()),
300  
-                                 formatDate(this.getMtime())));
301  
-        }
302  
-        else if (this.getCtime() != info.getCtime()) {
303  
-            changes.add(new Diff("Ctime",
304  
-                                 formatDate(info.getCtime()),
305  
-                                 formatDate(this.getCtime())));
306  
-        }
307  
-        else {
308  
-            //no point in checking the rest if all times are the same.
309  
-            //or should we include atime in the diff?
  295
+		ArrayList changes = new ArrayList();
  296
+
  297
+		if (this.getMtime() != info.getMtime()) {
  298
+			changes.add(new Diff("Mtime", formatDate(info.getMtime()),
  299
+					formatDate(this.getMtime())));
  300
+		} else if (this.getCtime() != info.getCtime()) {
  301
+			changes.add(new Diff("Ctime", formatDate(info.getCtime()),
  302
+					formatDate(this.getCtime())));
  303
+		}
  304
+
  305
+		if (this.getPermissions() != info.getPermissions()) {
  306
+			changes.add(new Diff("Perms", info.getPermissionsString(), this
  307
+					.getPermissionsString()));
  308
+		}
  309
+
  310
+		if (this.getType() != info.getType()) {
  311
+			changes.add(new Diff("Type", info.getTypeString(), this
  312
+					.getTypeString()));
  313
+		}
  314
+
  315
+		if (this.getUid() != info.getUid()) {
  316
+			changes.add(new Diff("Uid", info.getUid(), this.getUid()));
  317
+		}
  318
+
  319
+		if (this.getGid() != info.getGid()) {
  320
+			changes.add(new Diff("Gid", info.getGid(), this.getGid()));
  321
+		}
  322
+
  323
+		if (this.getSize() != info.getSize()) {
  324
+			changes.add(new Diff("Size", info.getSize(), this.getSize()));
  325
+		}
  326
+
  327
+		if (!OperatingSystem.IS_WIN32) {
  328
+			if (this.getInode() != info.getInode()) {
  329
+				changes.add(new Diff("Inode", info.getInode(), this.getInode()));
  330
+			}
  331
+
  332
+			if (this.getDevice() != info.getDevice()) {
  333
+				changes.add(new Diff("Device", info.getDevice(), this
  334
+						.getDevice()));
  335
+			}
  336
+
  337
+			if (this.getNlink() != info.getNlink()) {
  338
+				changes.add(new Diff("Nlink", info.getNlink(), this.getNlink()));
  339
+			}
  340
+        }
  341
+
  342
+        /* if changes were not detected then return empty String */
  343
+        if (changes.isEmpty()){
310 344
             return "";
311 345
         }
312  
-
313  
-        if (this.getPermissions() != info.getPermissions()) {
314  
-            changes.add(new Diff("Perms",
315  
-                                 info.getPermissionsString(),
316  
-                                 this.getPermissionsString()));
317  
-        }
318  
-
319  
-        if (this.getType() != info.getType()) {
320  
-            changes.add(new Diff("Type",
321  
-                                 info.getTypeString(),
322  
-                                 this.getTypeString()));
323  
-        }
324  
-
325  
-        if (this.getUid() != info.getUid()) {
326  
-            changes.add(new Diff("Uid",
327  
-                                 info.getUid(),
328  
-                                 this.getUid()));
329  
-        }
330  
-
331  
-        if (this.getGid() != info.getGid()) {
332  
-            changes.add(new Diff("Gid",
333  
-                                 info.getGid(),
334  
-                                 this.getGid()));
335  
-        }
336  
-
337  
-        if (this.getSize() != info.getSize()) {
338  
-            changes.add(new Diff("Size",
339  
-                                 info.getSize(),
340  
-                                 this.getSize()));
341  
-        }
342  
-
343  
-        if (!OperatingSystem.IS_WIN32) {
344  
-            if (this.getInode() != info.getInode()) {
345  
-                changes.add(new Diff("Inode",
346  
-                                     info.getInode(),
347  
-                                     this.getInode()));
348  
-            }
349  
-
350  
-            if (this.getDevice() != info.getDevice()) {
351  
-                changes.add(new Diff("Device",
352  
-                                     info.getDevice(),
353  
-                                     this.getDevice()));
354  
-            }
355  
-
356  
-            if (this.getNlink() != info.getNlink()) {
357  
-                changes.add(new Diff("Nlink",
358  
-                                     info.getNlink(),
359  
-                                     this.getNlink()));
360  
-            }
361  
-        }
362  
-
  346
+        
363 347
         StringBuffer sb = format(changes);
364 348
         if (this.dirStatEnabled) {
365 349
             sb.append(diff(info.stat));
@@ -389,7 +373,9 @@ public boolean modified()
389 373
 
390 374
         stat();
391 375
 
392  
-        return this.mtime != oldInfo.mtime;
  376
+        boolean isModified = isModified(this.oldInfo);
  377
+        
  378
+        return isModified;
393 379
     }
394 380
 
395 381
     public boolean changed()
@@ -455,4 +441,49 @@ static FileInfo fetchLinkInfo(Sigar sigar, String name)
455 441
 
456 442
         return fetchInfo(sigar, name, false);
457 443
     }
  444
+    
  445
+    private boolean isModified(FileInfo info){
  446
+    	/* Check modified time */
  447
+		if (this.getMtime() != info.getMtime()) {
  448
+			return true;
  449
+		} else if (this.getCtime() != info.getCtime()) {
  450
+			return true;
  451
+		}
  452
+		
  453
+		if (this.getPermissions() != info.getPermissions()) {
  454
+			return true;
  455
+		}
  456
+
  457
+		if (this.getType() != info.getType()) {
  458
+			return true;
  459
+		}
  460
+
  461
+		if (this.getUid() != info.getUid()) {
  462
+			return true;
  463
+		}
  464
+
  465
+		if (this.getGid() != info.getGid()) {
  466
+			return true;
  467
+		}
  468
+
  469
+		if (this.getSize() != info.getSize()) {
  470
+			return true;
  471
+		}
  472
+
  473
+		if (!OperatingSystem.IS_WIN32) {
  474
+			if (this.getInode() != info.getInode()) {
  475
+				return true;
  476
+			}
  477
+
  478
+			if (this.getDevice() != info.getDevice()) {
  479
+				return true;
  480
+			}
  481
+
  482
+			if (this.getNlink() != info.getNlink()) {
  483
+				return true;
  484
+			}
  485
+		}
  486
+
  487
+		return false;
  488
+	}
458 489
 }
55  bindings/java/src/org/hyperic/sigar/Sigar.java
@@ -42,6 +42,8 @@
42 42
     private static String loadError = null;
43 43
 
44 44
     public static final long FIELD_NOTIMPL = -1;
  45
+    public static final int PID_PROC_CPU_CACHE = 1;
  46
+    public static final int PID_PROC_IO_CACHE = 2;
45 47
 
46 48
     /**
47 49
      * The Sigar java version.
@@ -440,7 +442,7 @@ else if (Character.isDigit(pid.charAt(0))) {
440 442
             return this.processFinder.findSingleProcess(pid);
441 443
         }
442 444
     }
443  
-
  445
+    
444 446
     /**
445 447
      * Get process memory info.
446 448
      * @param pid The process id.
@@ -639,6 +641,57 @@ public long getProcPort(String protocol, String port)
639 641
     }
640 642
 
641 643
     /**
  644
+     * Get process disk IO info.
  645
+     * @param pid THe process id.
  646
+     * @exception SigarException on failure.
  647
+     */
  648
+    public ProcDiskIO getProcDiskIO(long pid) throws SigarException {
  649
+    	try {
  650
+    		return ProcDiskIO.fetch(this, pid);
  651
+    	} catch (UnsatisfiedLinkError linkErrorException) {
  652
+    		// We want to handle exceptions gracefully even if the linked
  653
+    		// shared library is older and isn't compiled with the ProcDiskIO APIs.
  654
+    		// The downside of this is that we throw SigarNotImplemented exception
  655
+    		// also when the shared library can't be loaded.
  656
+    		SigarException sigarException = new SigarNotImplementedException();
  657
+    		sigarException.initCause(linkErrorException);
  658
+    		throw sigarException;
  659
+    	}
  660
+    }
  661
+
  662
+    public ProcDiskIO getProcDiskIO(String pid) throws SigarException {
  663
+    	return getProcDiskIO(convertPid(pid));
  664
+    }
  665
+
  666
+   /**
  667
+     * Get process cumulative disk IO info.
  668
+     * @param pid THe process id.
  669
+     * @exception SigarException on failure.
  670
+     */
  671
+    public ProcCumulativeDiskIO getProcCumulativeDiskIO(long pid) throws SigarException {
  672
+        try {
  673
+                return ProcCumulativeDiskIO.fetch(this, pid);
  674
+        } catch (UnsatisfiedLinkError linkErrorException) {
  675
+                // We want to handle exceptions gracefully even if the linked
  676
+                // shared library is older and isn't compiled with the ProcDiskIO APIs.
  677
+                // The downside of this is that we throw SigarNotImplemented exception
  678
+                // also when the shared library can't be loaded.
  679
+                SigarException sigarException = new SigarNotImplementedException();
  680
+                sigarException.initCause(linkErrorException);
  681
+                throw sigarException;
  682
+        }
  683
+    }
  684
+
  685
+  public ProcCumulativeDiskIO getProcCumulativeDiskIO(String pid) throws SigarException {
  686
+        return getProcCumulativeDiskIO(convertPid(pid));
  687
+  }
  688
+
  689
+  public DumpPidCache dumpPidCache() throws SigarException {
  690
+       return DumpPidCache.fetch(this);
  691
+  }
  692
+
  693
+
  694
+    /**
642 695
      * Get the cumulative cpu time for the calling thread.
643 696
      */
644 697
     public ThreadCpu getThreadCpu() throws SigarException {
10  bindings/java/src/org/hyperic/sigar/SigarProxy.java
@@ -106,6 +106,16 @@
106 106
 
107 107
     public long getProcPort(String protocol, String port) throws SigarException;
108 108
 
  109
+    public ProcDiskIO getProcDiskIO(long pid) throws SigarException;
  110
+
  111
+    public ProcDiskIO getProcDiskIO(String pid) throws SigarException;
  112
+
  113
+    public ProcCumulativeDiskIO getProcCumulativeDiskIO(long pid) throws SigarException;
  114
+
  115
+    public ProcCumulativeDiskIO getProcCumulativeDiskIO(String pid) throws SigarException;
  116
+
  117
+    public DumpPidCache dumpPidCache() throws SigarException;
  118
+
109 119
     public FileSystem[] getFileSystemList() throws SigarException;
110 120
 
111 121
     public FileSystemMap getFileSystemMap() throws SigarException;
51  bindings/java/src/org/hyperic/sigar/cmd/PidCacheInfo.java
... ...
@@ -0,0 +1,51 @@
  1
+/*
  2
+ * Copyright (c) 2006-2007 Hyperic, Inc.
  3
+ *
  4
+ * Licensed under the Apache License, Version 2.0 (the "License");
  5
+ * you may not use this file except in compliance with the License.
  6
+ * You may obtain a copy of the License at
  7
+ *
  8
+ *     http://www.apache.org/licenses/LICENSE-2.0
  9
+ *
  10
+ * Unless required by applicable law or agreed to in writing, software
  11
+ * distributed under the License is distributed on an "AS IS" BASIS,
  12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13
+ * See the License for the specific language governing permissions and
  14
+ * limitations under the License.
  15
+ */
  16
+
  17
+package org.hyperic.sigar.cmd;
  18
+
  19
+import org.hyperic.sigar.SigarException;
  20
+import org.hyperic.sigar.SigarPermissionDeniedException;
  21
+
  22
+/**
  23
+ * Display all pid cache information.
  24
+ */
  25
+public class PidCacheInfo extends SigarCommandBase {
  26
+
  27
+
  28
+    public PidCacheInfo(Shell shell) {
  29
+        super(shell);
  30
+    }
  31
+
  32
+    public PidCacheInfo() {
  33
+        super();
  34
+    }
  35
+
  36
+    protected boolean validateArgs(String[] args) {
  37
+        return true;
  38
+    }
  39
+
  40
+    public String getUsageShort() {
  41
+        return "Display cache info for CPU cache and for IO cache";
  42
+    }
  43
+
  44
+    public boolean isPidCompleter() {
  45
+        return false;
  46
+    }
  47
+
  48
+    public void output(String[] args) throws SigarException {
  49
+        sigar.dumpPidCache();
  50
+    }
  51
+}
8  bindings/java/src/org/hyperic/sigar/cmd/ProcInfo.java
@@ -100,6 +100,14 @@ public void output(String pid) throws SigarException {
100 100
         try {
101 101
             println("credname=" + sigar.getProcCredName(pid));
102 102
         } catch (SigarException e) {}
  103
+        try {
  104
+        	println("diskio=" + sigar.getProcDiskIO(pid));
  105
+        } catch (SigarException e) {}
  106
+
  107
+ 	try {
  108
+                println("cumulative diskio=" + sigar.getProcCumulativeDiskIO(pid));
  109
+        } catch (SigarException e) {}
  110
+
103 111
     }
104 112
 
105 113
     public static void main(String[] args) throws Exception {
2  bindings/java/src/org/hyperic/sigar/cmd/Shell.java
@@ -103,6 +103,8 @@ public void registerCommands() throws ShellCommandInitException {
103 103
         registerCommandHandler("time", new Time(this));
104 104
         registerCommandHandler("ulimit", new Ulimit(this));
105 105
         registerCommandHandler("who", new Who(this));
  106
+        registerCommandHandler("pid_cache_info", new PidCacheInfo(this));
  107
+
106 108
         if (SigarLoader.IS_WIN32) {
107 109
             registerCommandHandler("service", new Win32Service(this));
108 110
             registerCommandHandler("fversion", new FileVersionInfo(this));
16  bindings/java/src/org/hyperic/sigar/jmx/SigarProcess.java
@@ -20,6 +20,7 @@
20 20
 import org.hyperic.sigar.ProcFd;
21 21
 import org.hyperic.sigar.ProcMem;
22 22
 import org.hyperic.sigar.ProcUtil;
  23
+import org.hyperic.sigar.ProcDiskIO;
23 24
 import org.hyperic.sigar.Sigar;
24 25
 import org.hyperic.sigar.SigarException;
25 26
 import org.hyperic.sigar.SigarProxy;
@@ -76,6 +77,16 @@ private synchronized ProcCpu getCpu() {
76 77
         }   
77 78
     }
78 79
 
  80
+
  81
+    private synchronized ProcDiskIO getDiskIO() {
  82
+	try {
  83
+            return this.sigar.getProcDiskIO(getPid());
  84
+        } catch (SigarException e) {
  85
+            throw unexpectedError("DiskIO", e);
  86
+        }
  87
+    }
  88
+
  89
+
79 90
     private synchronized ProcFd getFd() throws SigarException {
80 91
         return this.sigar.getProcFd(getPid());
81 92
     }
@@ -153,4 +164,9 @@ public Long getOpenFd() {
153 164
             return NOTIMPL;
154 165
         }
155 166
     }
  167
+
  168
+     public Double getBytesReadWriteTotal() {
  169
+        return new Double(getDiskIO().getBytesTotal());
  170
+    }
  171
+
156 172
 }
2  bindings/java/src/org/hyperic/sigar/jmx/SigarProcessMBean.java
@@ -44,4 +44,6 @@
44 44
     public Double getCpuUsage();
45 45
 
46 46
     public Long getOpenFd();
  47
+    
  48
+    public Double getBytesReadWriteTotal();
47 49
 }
8  bindings/java/src/org/hyperic/sigar/test/TestFileInfo.java
@@ -133,11 +133,11 @@ public void testCreate() throws Exception {
133 133
         tmp.deleteOnExit();
134 134
         traceln("TMP=" + file);
135 135
 
136  
-        try {
  136
+      //  try {
137 137
             //stat() mtime is in seconds, this happens to quick to detect change.
138  
-            Thread.sleep(1000 * 1);
139  
-        } catch (InterruptedException e) {
140  
-        }
  138
+            //Thread.sleep(1000 * 1);
  139
+      //  } catch (InterruptedException e) {
  140
+       // }
141 141
 
142 142
         try {
143 143
             FileInfo info = sigar.getFileInfo(file);
57  bindings/java/src/org/hyperic/sigar/test/TestProcDiskIO.java
... ...
@@ -0,0 +1,57 @@
  1
+package org.hyperic.sigar.test;
  2
+
  3
+import org.hyperic.sigar.ProcDiskIO;
  4
+import org.hyperic.sigar.Sigar;
  5
+import org.hyperic.sigar.SigarException;
  6
+
  7
+public class TestProcDiskIO extends SigarTestCase {
  8
+
  9
+	public TestProcDiskIO(String name) {
  10
+		super(name);
  11
+	}
  12
+	
  13
+	
  14
+    private void traceDiskIO(Sigar sigar, long pid) throws Exception {
  15
+        ProcDiskIO procDiskIO;
  16
+
  17
+        try {
  18
+            procDiskIO = sigar.getProcDiskIO(pid);
  19
+        } catch (SigarException e) {
  20
+            traceln("pid " + pid + ": " + e.getMessage());
  21
+            // throw e;
  22
+            return;
  23
+        }
  24
+
  25
+        long bytesRead = procDiskIO.getBytesRead();
  26
+        long bytesWritten = procDiskIO.getBytesWritten();
  27
+        long bytesTotal = procDiskIO.getBytesTotal();
  28
+        
  29
+        traceln("Pid=" + pid);
  30
+        traceln("Bytes Read=" + Sigar.formatSize(bytesRead));
  31
+        traceln("Bytes Written=" + Sigar.formatSize(bytesWritten));
  32
+        traceln("Bytes Total=" + Sigar.formatSize(bytesTotal));
  33
+        
  34
+        if (bytesRead != -1 && bytesWritten != -1 && bytesTotal != -1) {
  35
+        	assertTrue("Bytes total should equal bytesRead + bytesWritten",
  36
+        			    (bytesTotal == bytesRead + bytesWritten));
  37
+        }
  38
+    }
  39
+
  40
+    public void testCreate() throws Exception {
  41
+        Sigar sigar = getSigar();
  42
+
  43
+        boolean caughtException = false;
  44
+        try {
  45
+            sigar.getProcDiskIO(getInvalidPid());
  46
+        } catch (SigarException e) {
  47
+        	caughtException = true;
  48
+        }
  49
+        assertTrue("Test on invalid PID should have thrown an exception.", caughtException);
  50
+
  51
+        long[] pids = sigar.getProcList();
  52
+        for (int i=0; i<pids.length; i++) {
  53
+            traceDiskIO(sigar, pids[i]);
  54
+        }
  55
+    }
  56
+
  57
+}
41  include/sigar.h
@@ -293,6 +293,47 @@ SIGAR_DECLARE(int) sigar_proc_mem_get(sigar_t *sigar, sigar_pid_t pid,
293 293
                                       sigar_proc_mem_t *procmem);
294 294
 
295 295
 typedef struct {
  296
+     sigar_uint64_t 
  297
+        bytes_read,
  298
+        bytes_written,
  299
+        bytes_total;
  300
+} sigar_proc_disk_io_t;
  301
+
  302
+SIGAR_DECLARE(int) sigar_proc_disk_io_get(sigar_t *sigar, sigar_pid_t pid,
  303
+                                          sigar_proc_disk_io_t *proc_disk_io);
  304
+
  305
+typedef struct {
  306
+    sigar_uint64_t
  307
+        bytes_read,
  308
+        bytes_written,
  309
+        bytes_total;
  310
+    sigar_uint64_t last_time;
  311
+    sigar_uint64_t
  312
+        bytes_read_diff,
  313
+        bytes_written_diff,
  314
+        bytes_total_diff;
  315
+} sigar_cached_proc_disk_io_t;
  316
+
  317
+
  318
+typedef struct {
  319
+    sigar_uint64_t
  320
+        bytes_read,
  321
+        bytes_written,
  322
+        bytes_total;
  323
+} sigar_proc_cumulative_disk_io_t;
  324
+
  325
+SIGAR_DECLARE(int) sigar_proc_cumulative_disk_io_get(sigar_t *sigar, sigar_pid_t pid,
  326
+                                          sigar_proc_cumulative_disk_io_t *proc_cumulative_disk_io);
  327
+
  328
+
  329
+typedef struct  { 
  330
+    sigar_uint64_t dummy;
  331
+}sigar_dump_pid_cache_t;
  332
+
  333
+SIGAR_DECLARE(int) sigar_dump_pid_cache_get(sigar_t *sigar, sigar_dump_pid_cache_t *info);
  334
+
  335
+
  336
+typedef struct {
296 337
     sigar_uid_t uid;
297 338
     sigar_gid_t gid;
298 339
     sigar_uid_t euid;
9  include/sigar_private.h
@@ -68,7 +68,8 @@
68 68
    sigar_cache_t *proc_cpu; \
69 69
    sigar_cache_t *net_listen; \
70 70
    sigar_cache_t *net_services_tcp; \
71  
-   sigar_cache_t *net_services_udp
  71
+   sigar_cache_t *net_services_udp;\
  72
+   sigar_cache_t *proc_io
72 73
 
73 74
 #if defined(WIN32)
74 75
 #   define SIGAR_INLINE __inline
@@ -398,11 +399,15 @@ int sigar_get_iftype(const char *name, int *type, int *inst);
398 399
 #define SIGAR_NIC_SIT      "IPv6-in-IPv4"
399 400
 #define SIGAR_NIC_IRDA     "IrLAP"
400 401
 #define SIGAR_NIC_EC       "Econet"
401  
-
  402
+#define PID_CACHE_CLEANUP_PERIOD 1000*60*10 /* 10 minutes */
  403
+#define PID_CACHE_ENTRY_EXPIRE_PERIOD 1000*60*20 /* 20 minutes */
402 404
 #ifndef WIN32
403 405
 #include <netdb.h>
404 406
 #endif
405 407
 
  408
+#define PROC_PID_CPU_CACHE 1
  409
+#define PROC_PID_IO_CACHE 2
  410
+
406 411
 #define SIGAR_HOSTENT_LEN 1024
407 412
 #if defined(_AIX)
408 413
 #define SIGAR_HAS_HOSTENT_DATA
6  include/sigar_util.h
@@ -170,15 +170,21 @@ struct sigar_cache_entry_t {
170 170
     sigar_cache_entry_t *next;
171 171
     sigar_uint64_t id;
172 172
     void *value;
  173
+    sigar_uint64_t last_access_time;
173 174
 };
174 175
 
175 176
 typedef struct {
176 177
     sigar_cache_entry_t **entries;
177 178
     unsigned int count, size;
178 179
     void (*free_value)(void *ptr);
  180
+    sigar_uint64_t entry_expire_period;
  181
+    sigar_uint64_t cleanup_period_millis;
  182
+    sigar_uint64_t last_cleanup_time;
179 183
 } sigar_cache_t;
180 184
 
181 185
 sigar_cache_t *sigar_cache_new(int size);
  186
+sigar_cache_t *sigar_expired_cache_new(int size, sigar_uint64_t cleanup_period_millis, sigar_uint64_t entry_expire_period);
  187
+void sigar_cache_dump(sigar_cache_t *table);
182 188
 
183 189
 sigar_cache_entry_t *sigar_cache_get(sigar_cache_t *table,
184 190
                                      sigar_uint64_t key);
17  src/os/aix/aix_sigar.c
@@ -754,6 +754,23 @@ int sigar_proc_mem_get(sigar_t *sigar, sigar_pid_t pid,
754 754
     return SIGAR_OK;
755 755
 }
756 756
 
  757
+int sigar_proc_cumulative_disk_io_get(sigar_t *sigar, sigar_pid_t pid,
  758
+                           sigar_proc_cumulative_disk_io_t *cumulative_proc_disk_io)
  759
+{
  760
+    int status = sigar_getprocs(sigar, pid);
  761
+    struct procsinfo64 *pinfo = sigar->pinfo;
  762
+
  763
+    if (status != SIGAR_OK) {
  764
+        return status;
  765
+    }
  766
+    cumulative_proc_disk_io->bytes_read = SIGAR_FIELD_NOTIMPL;
  767
+    cumulative_proc_disk_io->bytes_written =  SIGAR_FIELD_NOTIMPL;
  768
+    cumulative_proc_disk_io->bytes_total = pinfo->pi_ioch;
  769
+
  770
+    return SIGAR_OK;
  771
+}
  772
+
  773
+
757 774
 int sigar_proc_cred_get(sigar_t *sigar, sigar_pid_t pid,
758 775
                         sigar_proc_cred_t *proccred)
759 776
 {
7  src/os/darwin/darwin_sigar.c
@@ -1241,6 +1241,13 @@ int sigar_proc_mem_get(sigar_t *sigar, sigar_pid_t pid,
1241 1241
     return SIGAR_OK;
1242 1242
 }
1243 1243
 
  1244
+int sigar_proc_cumulative_disk_io_get(sigar_t *sigar, sigar_pid_t pid,
  1245
+                           sigar_proc_cumulative_disk_io_t *proc_cumulative_disk_io)
  1246
+{
  1247
+    return SIGAR_ENOTIMPL;
  1248
+}
  1249
+
  1250
+
1244 1251
 int sigar_proc_cred_get(sigar_t *sigar, sigar_pid_t pid,
1245 1252
                         sigar_proc_cred_t *proccred)
1246 1253
 {
19  src/os/hpux/hpux_sigar.c
@@ -307,6 +307,25 @@ int sigar_proc_mem_get(sigar_t *sigar, sigar_pid_t pid,
307 307
     return SIGAR_OK;
308 308
 }
309 309
 
  310
+int sigar_proc_cumulative_disk_io_get(sigar_t *sigar, sigar_pid_t pid, 
  311
+                           sigar_proc_cumulative_disk_io_t *proc_cumulative_disk_io)
  312
+{
  313
+
  314
+    int status = sigar_pstat_getproc(sigar, pid);
  315
+    struct pst_status *pinfo = sigar->pinfo;
  316
+
  317
+    if (status != SIGAR_OK) {
  318
+        return status;
  319
+    }
  320
+    proc_cumulative_disk_io->bytes_read = SIGAR_FIELD_NOTIMPL; 
  321
+    proc_cumulative_disk_io->bytes_written = SIGAR_FIELD_NOTIMPL; 
  322
+    proc_cumulative_disk_io->bytes_total =  pinfo->pst_ioch;
  323
+
  324
+
  325
+   return SIGAR_OK;
  326
+}
  327
+
  328
+
310 329
 int sigar_proc_cred_get(sigar_t *sigar, sigar_pid_t pid,
311 330
                         sigar_proc_cred_t *proccred)
312 331
 {
28  src/os/linux/linux_sigar.c
@@ -768,6 +768,34 @@ int sigar_proc_mem_get(sigar_t *sigar, sigar_pid_t pid,
768 768
     return SIGAR_OK;
769 769
 }
770 770
 
  771
+SIGAR_INLINE sigar_uint64_t get_named_proc_token(char *buffer,
  772
+                                                 char *token) {
  773
+  char *ptr = strstr(buffer, token);
  774
+  if (!ptr) {
  775
+    return SIGAR_FIELD_NOTIMPL;
  776
+  }
  777
+  ptr = sigar_skip_token(ptr);
  778
+  return sigar_strtoul(ptr);
  779
+}
  780
+
  781
+int sigar_proc_cumulative_disk_io_get(sigar_t *sigar, sigar_pid_t pid,
  782
+                           sigar_proc_cumulative_disk_io_t *proc_cumulative_disk_io)
  783
+{
  784
+    char buffer[BUFSIZ];
  785
+    
  786
+    int status = SIGAR_PROC_FILE2STR(buffer, pid, "/io");
  787
+    
  788
+    if (status != SIGAR_OK) {
  789
+        return status;
  790
+    }
  791
+
  792
+    proc_cumulative_disk_io->bytes_read = get_named_proc_token(buffer, "\nread_bytes");
  793
+    proc_cumulative_disk_io->bytes_written = get_named_proc_token(buffer, "\nwrite_bytes");
  794
+    proc_cumulative_disk_io->bytes_total = proc_cumulative_disk_io->bytes_read + proc_cumulative_disk_io->bytes_written;
  795
+
  796
+    return SIGAR_OK;
  797
+}
  798
+
771 799
 #define NO_ID_MSG "[proc_cred] /proc/%lu" PROC_PSTATUS " missing "
772 800
 
773 801
 int sigar_proc_cred_get(sigar_t *sigar, sigar_pid_t pid,
17  src/os/solaris/solaris_sigar.c
@@ -407,7 +407,7 @@ int sigar_cpu_get(sigar_t *sigar, sigar_cpu_t *cpu)
407 407
         cpu->idle  += xcpu->idle;
408 408
         cpu->nice  += xcpu->nice;
409 409
         cpu->wait  += xcpu->wait;
410  
-        cpu->total = xcpu->total;
  410
+        cpu->total += xcpu->total;
411 411
     }
412 412
 
413 413
     return SIGAR_OK;
@@ -719,6 +719,21 @@ int sigar_proc_mem_get(sigar_t *sigar, sigar_pid_t pid,
719 719
     return SIGAR_OK;
720 720
 }
721 721
 
  722
+int sigar_proc_cumulative_disk_io_get(sigar_t *sigar, sigar_pid_t pid, 
  723
+                           sigar_proc_cumulative_disk_io_t *proc_cumulative_disk_io)
  724
+{
  725
+   prusage_t usage;
  726
+   int status;
  727
+   if ((status = sigar_proc_usage_get(sigar, &usage, pid)) != SIGAR_OK) {
  728
+        return status;
  729
+   }
  730
+   proc_cumulative_disk_io->bytes_read = SIGAR_FIELD_NOTIMPL;
  731
+   proc_cumulative_disk_io->bytes_written = SIGAR_FIELD_NOTIMPL;
  732
+   proc_cumulative_disk_io->bytes_total =  usage.pr_ioch;
  733
+
  734
+    return SIGAR_OK;
  735
+}
  736
+
722 737
 int sigar_proc_cred_get(sigar_t *sigar, sigar_pid_t pid,
723 738
                         sigar_proc_cred_t *proccred)
724 739
 {
2  src/os/win32/sigar_os.h
@@ -345,6 +345,8 @@ typedef struct {
345 345
     sigar_uint64_t handles;
346 346
     sigar_uint64_t threads;
347 347
     sigar_uint64_t page_faults;
  348
+	sigar_uint64_t bytes_read;
  349
+	sigar_uint64_t bytes_written;
348 350
 } sigar_win32_pinfo_t;
349 351
 
350 352
 typedef struct {
56  src/os/win32/win32_sigar.c
@@ -62,6 +62,8 @@ typedef enum {
62 62
 #define PERF_TITLE_PPID       1410
63 63
 #define PERF_TITLE_PRIORITY   682
64 64
 #define PERF_TITLE_START_TIME 684
  65
+#define PERF_TITLE_IO_READ_BYTES_SEC	1420
  66
+#define PERF_TITLE_IO_WRITE_BYTES_SEC	1422
65 67
 
66 68
 typedef enum {
67 69
     PERF_IX_CPUTIME,
@@ -74,6 +76,8 @@ typedef enum {
74 76
     PERF_IX_PPID,
75 77
     PERF_IX_PRIORITY,
76 78
     PERF_IX_START_TIME,
  79
+	PERF_IX_IO_READ_BYTES_SEC,
  80
+	PERF_IX_IO_WRITE_BYTES_SEC,
77 81
     PERF_IX_MAX
78 82
 } perf_proc_offsets_t;
79 83
 
@@ -1210,6 +1214,23 @@ SIGAR_DECLARE(int) sigar_proc_mem_get(sigar_t *sigar, sigar_pid_t pid,
1210 1214
     return SIGAR_OK;
1211 1215
 }
1212 1216
 
  1217
+SIGAR_DECLARE(int) sigar_proc_cumulative_disk_io_get(sigar_t *sigar, sigar_pid_t pid,
  1218
+                                          sigar_proc_cumulative_disk_io_t *proc_cumulative_disk_io)
  1219
+{
  1220
+    int status = get_proc_info(sigar, pid);
  1221
+    sigar_win32_pinfo_t *pinfo = &sigar->pinfo;
  1222
+
  1223
+    if (status != SIGAR_OK) {
  1224
+        return status;
  1225
+    }
  1226
+
  1227
+    proc_cumulative_disk_io->bytes_read = pinfo->bytes_read;
  1228
+    proc_cumulative_disk_io->bytes_written = pinfo->bytes_written;
  1229
+    proc_cumulative_disk_io->bytes_total = proc_cumulative_disk_io->bytes_read + proc_cumulative_disk_io->bytes_written;
  1230
+
  1231
+    return SIGAR_OK;
  1232
+}
  1233
+
1213 1234
 #define TOKEN_DAC (STANDARD_RIGHTS_READ | READ_CONTROL | TOKEN_QUERY)
1214 1235
 
1215 1236
 SIGAR_DECLARE(int)
@@ -1441,6 +1462,12 @@ static int get_proc_info(sigar_t *sigar, sigar_pid_t pid)
1441 1462
           case PERF_TITLE_START_TIME:
1442 1463
             perf_offsets[PERF_IX_START_TIME] = offset;
1443 1464
             break;
  1465
+		  case PERF_TITLE_IO_READ_BYTES_SEC:
  1466
+			perf_offsets[PERF_IX_IO_READ_BYTES_SEC] = offset;
  1467
+			break;
  1468
+		  case PERF_TITLE_IO_WRITE_BYTES_SEC:
  1469
+			perf_offsets[PERF_IX_IO_WRITE_BYTES_SEC] = offset;
  1470
+			break;
1444 1471
         }
1445 1472
     }
1446 1473
 
@@ -1466,6 +1493,8 @@ static int get_proc_info(sigar_t *sigar, sigar_pid_t pid)
1466 1493
         pinfo->handles  = PERF_VAL(PERF_IX_HANDLE_CNT);
1467 1494
         pinfo->threads  = PERF_VAL(PERF_IX_THREAD_CNT);
1468 1495
         pinfo->page_faults = PERF_VAL(PERF_IX_PAGE_FAULTS);
  1496
+		pinfo->bytes_read = PERF_VAL(PERF_IX_IO_READ_BYTES_SEC);
  1497
+		pinfo->bytes_written = PERF_VAL(PERF_IX_IO_WRITE_BYTES_SEC);
1469 1498
 
1470 1499
         return SIGAR_OK;
1471 1500
     }
@@ -3693,6 +3722,7 @@ int sigar_who_list_get_win32(sigar_t *sigar,
3693 3722
 #define SIGAR_ARCH "x86"
3694 3723
 #endif
3695 3724
 
  3725
+
3696 3726
 int sigar_os_sys_info_get(sigar_t *sigar,
3697 3727
                           sigar_sys_info_t *sysinfo)
3698 3728
 {
@@ -3700,7 +3730,7 @@ int sigar_os_sys_info_get(sigar_t *sigar,
3700 3730
     char *vendor_name, *vendor_version, *code_name=NULL;
3701 3731
 
3702 3732
     version.dwOSVersionInfoSize = sizeof(version);
3703  
-    GetVersionEx((OSVERSIONINFO *)&version);
  3733
+    GetVersionEx((OSVERSIONINFO *)&version); 
3704 3734
 
3705 3735
     if (version.dwMajorVersion == 4) {
3706 3736
         vendor_name = "Windows NT";
@@ -3740,11 +3770,25 @@ int sigar_os_sys_info_get(sigar_t *sigar,
3740 3770
                 code_name = "Vienna";
3741 3771
             }
3742 3772
         }
3743  
-        else {
3744  
-            vendor_name = "Windows 2008";
3745  
-            vendor_version = "2008";
3746  
-            code_name = "Longhorn Server";
3747  
-        }
  3773
+	else {
  3774
+             // not nt work station
  3775
+             if (version.dwMinorVersion == 0 || version.dwMinorVersion ==1) {
  3776
+            	vendor_name = "Windows 2008";
  3777
+            	vendor_version = "2008";
  3778
+	        code_name = "Longhorn Server";
  3779
+             }
  3780
+	     else  if (version.dwMinorVersion == 2) {
  3781
+ 	    	vendor_name = "Windows 2012";
  3782
+            	vendor_version = "2012";
  3783
+            	code_name = "Windows Server 8";
  3784
+	     }
  3785
+	     else {
  3786
+		// defaults
  3787
+		 vendor_name = "Windows Unknown";
  3788
+		 vendor_version = "2012";
  3789
+	     }
  3790
+	}
  3791
+
3748 3792
     }
3749 3793
 
3750 3794
     SIGAR_SSTRCPY(sysinfo->name, "Win32");
108  src/sigar.c
@@ -64,6 +64,7 @@ SIGAR_DECLARE(int) sigar_open(sigar_t **sigar)
64 64
         (*sigar)->net_listen = NULL;
65 65
         (*sigar)->net_services_tcp = NULL;
66 66
         (*sigar)->net_services_udp = NULL;
  67
+	(*sigar)->proc_io = NULL;
67 68
     }
68 69
 
69 70
     return status;
@@ -96,6 +97,11 @@ SIGAR_DECLARE(int) sigar_close(sigar_t *sigar)
96 97
     if (sigar->net_services_udp) {
97 98
         sigar_cache_destroy(sigar->net_services_udp);
98 99
     }
  100
+    if (sigar->proc_io) {
  101
+        sigar_cache_destroy(sigar->proc_io);
  102
+    }
  103
+
  104
+
99 105
 
100 106
     return sigar_os_close(sigar);
101 107
 }
@@ -123,7 +129,7 @@ SIGAR_DECLARE(int) sigar_proc_cpu_get(sigar_t *sigar, sigar_pid_t pid,
123 129
     int status;
124 130
 
125 131
     if (!sigar->proc_cpu) {
126  
-        sigar->proc_cpu = sigar_cache_new(128);
  132
+        sigar->proc_cpu = sigar_expired_cache_new(128, PID_CACHE_CLEANUP_PERIOD, PID_CACHE_ENTRY_EXPIRE_PERIOD);
127 133
     }
128 134
 
129 135
     entry = sigar_cache_get(sigar->proc_cpu, pid);
@@ -172,6 +178,106 @@ SIGAR_DECLARE(int) sigar_proc_cpu_get(sigar_t *sigar, sigar_pid_t pid,
172 178
 
173 179
     return SIGAR_OK;
174 180
 }
  181
+void copy_cached_disk_io_into_disk_io( sigar_cached_proc_disk_io_t *cached,  sigar_proc_disk_io_t *proc_disk_io) {
  182
+   proc_disk_io->bytes_read = cached->bytes_read_diff;
  183
+   proc_disk_io->bytes_written = cached->bytes_written_diff;
  184
+   proc_disk_io->bytes_total = cached->bytes_total_diff;
  185
+}
  186
+
  187
+sigar_uint64_t get_io_diff(sigar_uint64_t current_value, sigar_uint64_t prev_value,  sigar_uint64_t time_diff) {
  188
+   double io_diff;
  189
+   sigar_uint64_t int_io_diff;
  190
+   if ( current_value == SIGAR_FIELD_NOTIMPL ) {
  191
+      return SIGAR_FIELD_NOTIMPL;
  192
+   }
  193
+   io_diff = (( current_value - prev_value)/(double)time_diff)*SIGAR_MSEC;
  194
+   int_io_diff = (sigar_uint64_t)io_diff;
  195
+   if (int_io_diff >=0) {
  196
+      return int_io_diff;
  197
+   }
  198
+   return 0;
  199
+}
  200
+
  201
+void calculate_io_diff(sigar_proc_cumulative_disk_io_t * proc_disk_io, sigar_cached_proc_disk_io_t *cached,  sigar_uint64_t time_diff, int is_first_time) {
  202
+   /*calculate avg diff /read/write/total per second*/
  203
+   if (!is_first_time) {
  204
+       cached->bytes_written_diff = get_io_diff(proc_disk_io->bytes_written, cached->bytes_written, time_diff);
  205
+       cached->bytes_read_diff = get_io_diff(proc_disk_io->bytes_read, cached->bytes_read, time_diff);
  206
+       cached->bytes_total_diff = get_io_diff(proc_disk_io->bytes_total, cached->bytes_total, time_diff);
  207
+   }
  208
+   else {
  209
+       cached->bytes_total_diff =  cached->bytes_read_diff =  cached->bytes_written_diff = 0.0;
  210
+   }
  211
+   // now put in cache the current cumulative values
  212
+   cached->bytes_written = proc_disk_io->bytes_written;
  213
+   cached->bytes_read = proc_disk_io->bytes_read;
  214
+   cached->bytes_total = proc_disk_io->bytes_total;
  215
+}
  216
+
  217
+SIGAR_DECLARE(int) sigar_proc_disk_io_get(sigar_t *sigar, sigar_pid_t pid,
  218
+                                          sigar_proc_disk_io_t *proc_disk_io)
  219
+{
  220
+    sigar_cache_entry_t *entry;
  221
+    sigar_cached_proc_disk_io_t *prev;
  222
+    sigar_proc_cumulative_disk_io_t  cumulative_proc_disk_io;
  223
+    sigar_uint64_t time_now = sigar_time_now_millis();
  224
+    sigar_uint64_t time_diff;
  225
+    int status, is_first_time;
  226
+
  227
+    if (!sigar->proc_io) {
  228
+        sigar->proc_io =  sigar_expired_cache_new(128, PID_CACHE_CLEANUP_PERIOD, PID_CACHE_ENTRY_EXPIRE_PERIOD);
  229
+    }
  230
+
  231
+    entry = sigar_cache_get(sigar->proc_io, pid);
  232
+    if (entry->value) {
  233
+        prev = (sigar_cached_proc_disk_io_t *)entry->value;
  234
+    }
  235
+    else {
  236
+        prev = entry->value = malloc(sizeof(*prev));
  237
+        SIGAR_ZERO(prev);
  238
+    }
  239
+    is_first_time = (prev->last_time == 0);
  240
+    time_diff = time_now - prev->last_time;
  241
+
  242
+    if (time_diff < 1000) {
  243
+        /* we were just called within < 1 second ago. */
  244
+        copy_cached_disk_io_into_disk_io(prev, proc_disk_io);
  245
+	if (time_diff < 0) {
  246
+	   // something is wrong at least from now on the time will be ok
  247
+ 	   prev->last_time = time_now;
  248
+        }
  249
+        return SIGAR_OK;
  250
+    }
  251
+    prev->last_time = time_now;
  252
+
  253
+
  254
+    status =
  255
+        sigar_proc_cumulative_disk_io_get(sigar, pid,
  256
+                            &cumulative_proc_disk_io);
  257
+
  258
+    if (status != SIGAR_OK) {
  259
+        return status;
  260
+    }
  261
+    calculate_io_diff(&cumulative_proc_disk_io, prev, time_diff,  is_first_time);
  262
+    copy_cached_disk_io_into_disk_io(prev, proc_disk_io);
  263
+    return SIGAR_OK;
  264
+}
  265
+
  266
+void get_cache_info(sigar_cache_t * cache, char * name){
  267
+   if (cache == NULL) {
  268
+      return;
  269
+   }
  270
+
  271
+   printf("******** %s *********\n", name);
  272
+   sigar_cache_dump(cache);
  273
+}
  274
+
  275
+SIGAR_DECLARE(int) sigar_dump_pid_cache_get(sigar_t *sigar, sigar_dump_pid_cache_t *info) {
  276
+  
  277
+  get_cache_info(sigar->proc_cpu, "proc cpu cache");
  278
+  get_cache_info(sigar->proc_io, "proc io cache");
  279
+  return SIGAR_OK;
  280
+}
175 281
 
176 282
 SIGAR_DECLARE(int) sigar_proc_stat_get(sigar_t *sigar,
177 283
                                        sigar_proc_stat_t *procstat)
88  src/sigar_cache.c
@@ -35,7 +35,7 @@ static void free_value(void *ptr)
35 35
     free(ptr);
36 36
 }
37 37
 
38  
-sigar_cache_t *sigar_cache_new(int size)
  38
+sigar_cache_t *sigar_expired_cache_new(int size, sigar_uint64_t cleanup_period_millis, sigar_uint64_t entry_expire_period)
39 39
 {
40 40
     sigar_cache_t *table = malloc(sizeof(*table));
41 41
     table->count = 0;
@@ -43,16 +43,27 @@ sigar_cache_t *sigar_cache_new(int size)
43 43
     table->entries = malloc(ENTRIES_SIZE(size));
44 44
     memset(table->entries, '\0', ENTRIES_SIZE(size));
45 45
     table->free_value = free_value;
  46
+    table->cleanup_period_millis = cleanup_period_millis;
  47
+    table->last_cleanup_time = sigar_time_now_millis();
  48
+    table->entry_expire_period = entry_expire_period;
46 49
     return table;
47 50
 }
48 51
 
49  
-#ifdef DEBUG_CACHE
  52
+sigar_cache_t *sigar_cache_new(int size)
  53
+{
  54
+    return sigar_expired_cache_new(size, SIGAR_FIELD_NOTIMPL, SIGAR_FIELD_NOTIMPL);
  55
+}
  56
+
  57
+
  58
+/*#ifdef DEBUG_CACHE*/
50 59
 /* see how well entries are distributed */
51  
-static void sigar_cache_dump(sigar_cache_t *table)
  60
+void sigar_cache_dump(sigar_cache_t *table)