Skip to content

Instantly share code, notes, and snippets.

@miguelsaddress
Last active October 26, 2020 16:13
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save miguelsaddress/59e2c5fab39b48c071a2 to your computer and use it in GitHub Desktop.
Save miguelsaddress/59e2c5fab39b48c071a2 to your computer and use it in GitHub Desktop.
Fetch info about the memory of you machine with Scala
import scala.language.postfixOps
import scala.util.{Success, Failure}
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.duration._
import scala.concurrent._
import scala.util.Try
import sys.process._
import concurrent.Future
import concurrent.Promise
/**
* I am trying to use this to fetch info about memory in a mac os
* since it does not seem to hae a /proc/memInfo file I went for the
* approach of running top command in batch. This information is for
* educational purpose. Feel free to send me a better/correct approach
* to find out the ammount of total RAM of a machine and the available
* memory it has.
*/
trait MemInfoCommand {
private lazy val memLineValues = getMemLineValues()
def totalMemory = memLineValues._1 / (1024 * 1024)
def usedMemory = memLineValues._2 / (1024 * 1024)
def freeMemory = memLineValues._3 / (1024 * 1024)
def availableMemory = 0 //TODO
def getMemLineValues(): (Double, Double, Double) = {
val memFuture = Await.result(readMemLine, 5.second)
// KiB Mem: 8060048 total, 7586092 used, 473956 free, 672268 buffers
val res = memFuture.split(":")(1).split(",").map(x => x.trim.split(" ")(0).toDouble)
val (total, used, free) = (res(0), res(1), res(2))
(total, used, free)
}
def readMemLine(): Future[String] = {
val memLinePromise = Promise[String]()
Future {
//-b1 is for Linux and -l1 is for MacOS
val memLine = Try(("top -b1" #| "head -n 10" #| "grep Mem" !!).split("\n")(0))
//val memLine = Try(("top -l1" #| "head -n 10" #| "grep Mem" !!).split("\n")(0))
memLine match {
case Success(line) => memLinePromise.success(line)
case Failure(ex) => memLinePromise.failure(ex)
}
}
memLinePromise.future
}
override def toString = {
var str = f"Total Memory [$totalMemory%2.2f GB]\n"
str += f"Used Memory [$usedMemory%2.2f GB]\n"
str += f"Free Memory [$freeMemory%2.2f GB]\n"
str += f"Available Memory [$availableMemory%2.2f GB]\n"
str
}
}
object SystemInfo extends MemInfoCommand {
override def toString = {
super[MemInfoCommand].toString
}
}
println(SystemInfo)

#Problem faced

I am new to Scala and rusty in Java.

When running the top command it throws an exception that I am not capturing properly and I dont see where the error seems to be or how could I correct it. As you can see in the output, the results are given properly, but the exception is always thrown. Ideas?

I/O error Pipe closed for process: [top, -b1]
java.io.IOException: Pipe closed
	at java.io.PipedInputStream.checkStateForReceive(PipedInputStream.java:260)
	at java.io.PipedInputStream.awaitSpace(PipedInputStream.java:268)
	at java.io.PipedInputStream.receive(PipedInputStream.java:231)
	at java.io.PipedOutputStream.write(PipedOutputStream.java:149)
	at scala.sys.process.BasicIO$.loop$1(BasicIO.scala:236)
	at scala.sys.process.BasicIO$.transferFullyImpl(BasicIO.scala:242)
	at scala.sys.process.BasicIO$.transferFully(BasicIO.scala:223)
	at scala.sys.process.ProcessImpl$PipeThread.runloop(ProcessImpl.scala:159)
	at scala.sys.process.ProcessImpl$PipeSource.run(ProcessImpl.scala:179)
Total Memory [7,69 GB]
Used Memory [7,45 GB]
Free Memory [0,24 GB]
Available Memory [0,00 GB]
@JSantosP
Copy link

I haven't tried so far but, have you tried invoking .lines instead of .!! ? Since top command output is not finite, maybe using a Stream[String] would be a good idea...

@miguelsaddress
Copy link
Author

The use of .lines/ .lineStrem was solving the issue, thank you but it seems the main problem was a bad use of the top command parameters. When using the wrong top command but capturing the Stream[String], the execution was taking long time due to the nature of the top command in batches.

This solves it. Setting topas returning one and only batch
https://github.com/miguelsaddress/SystemInfoParser/blob/cf805e8eaa147ff84d4390c6bc06457fcd38dfac/src/main/scala/com/mamoreno/systemInfo/os/MacOsInfo.scala#L26
Thanks for your help ^_^

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment