Skip to content

Instantly share code, notes, and snippets.

@dpsoft
Last active December 19, 2017 19:56
Show Gist options
  • Save dpsoft/e38685836cf7553b4fc0a2fb6bd44d1c to your computer and use it in GitHub Desktop.
Save dpsoft/e38685836cf7553b4fc0a2fb6bd44d1c to your computer and use it in GitHub Desktop.
object Process {
type UserTime = Long
type KernelTime = Long
type StartTime = Long
//Hertz (number of clock ticks per second) of your system.
val Hz: Long = executeCmd("getconf CLK_TCK").map(_.toLong).getOrElse(100L)
/**
* A process is an instance of a computer program that is being executed.
*
* @param pid process Id.
* @param name process name.
* @param uTime CPU time spent in user code, measured in clock ticks.
* @param sTime CPU time spent in kernel code, measured in clock ticks.
* @param startTime Time when the process started, measured in clock ticks.
*/
case class Process(pid:Int, name:String, uTime:UserTime, sTime:KernelTime, startTime:StartTime) extends Ordered[Process] {
override def compare(that: Process): Int =
java.lang.Double.compare((that.uTime + that.sTime) / that.startTime.toDouble, (this.uTime + this.sTime) / this.startTime.toDouble)
}
/**
* A "jiffy" is a unit of CPU time.
*
* Exactly what it corresponds to in wall-clock time depends on the architecture and how your kernel is configured,
* but the important thing is that /proc/stat tells you how many jiffies the CPU has executed in total
* and /proc/<PID>/stat tells you how many jiffies have been executed by a single process.
*
* @param pid process id
* @return (UserTime, KernelTime, StartTime) in jiffies
*/
def jiffiesByProcess(pid: Long): (UserTime, KernelTime, StartTime) =
firstLineOf(s"/proc/$pid/stat").map { line =>
val values = line.split(" ")
val uTime = Option(values(13).toLong).getOrElse(0L) * 1000L / Hz
val sTime = Option(values(14).toLong).getOrElse(0L) * 1000L / Hz
val startTime = Option(values(21).toLong).getOrElse(0L) * 1000L / Hz
(uTime, sTime, startTime)
}.getOrElse((0L, 0L, 0L))
private def getProcessByPid(pid:Int):Option[Process] = {
firstLineOf(s"/proc/$pid/stat").map { line =>
val values = line.split(" ")
//See man proc for how to parse /proc/[pid]/stat
val name = values(1).replaceFirst("\\(", "").replace(")", "")
val (uTime, sTime, startTime) = jiffiesByProcess(pid)
Process(pid, name, uTime, sTime, System.currentTimeMillis() - startTime)
}
}
private def firstLineOf(f: String): Option[String] = {
val src = Source.fromFile(f)
try src.getLines.find(_ => true) finally {
src.close()
}
}
private def executeCmd(cmd:String): Option[String] = {
import sys.process._
Try((cmd !!).trim).toOption
}
}
object Pid {
val Digits: Pattern = Pattern.compile("\\d+")
/**
* Gets an Seq of integers in the /proc directory with only numeric digit
* filenames, corresponding to processes
*
* @return An Seq of integers that represents the process ids.
*/
def getAll: Seq[Int] = Option(new File("/proc").listFiles(new FileFilter() {
override def accept(file: File): Boolean =
Digits.matcher(file.getName).matches()
}).toList.map(_.getName.toInt)).getOrElse(Nil)
}
object Test extends App {
while(true) {
Thread.sleep(1000)
println(Pid.getAll.map(p => Process.getProcessByPid(p)).sorted.take(10))
}
}
//List(Some(Process(5549,Web,0,26673100,1513710984082)), Some(Process(31238,chrome,16024140,5713990,1513130505838)), Some(Process(24479,java,11861730,3099950,1512196478427)), Some(Process(1390,Xorg,6211480,4629680,1513710968296)), Some(Process(5057,firefox,6082530,2672330,1512697554152)), Some(Process(31317,chrome,5734670,2596150,1513130505249)), Some(Process(31778,chrome,5766340,513800,1513130498929)), Some(Process(1543,pulseaudio,2779660,2887010,1513710967307)), Some(Process(31807,chrome,4682150,616300,1513130494870)), Some(Process(1984,gnome-shell,3135200,423750,1512420219311)))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment