Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cpu & memory usage fixes #946

Merged
merged 14 commits into from
Jul 25, 2024
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions docs/api/dashboard/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2028,7 +2028,7 @@ These headers must be present in each request.
"rss": 171692,
"native_total_heap": 21872,
"native_free_heap": 1299,
"interval_config": 2000,
"interval": 2000,
"timestamp": "2024-05-03T23:34:17.607Z"
},
{
Expand All @@ -2039,7 +2039,7 @@ These headers must be present in each request.
"rss": 187708,
"native_total_heap": 24060,
"native_free_heap": 1171,
"interval_config": 2000,
"interval": 2000,
"timestamp": "2024-05-03T23:34:19.566Z"
},
{
Expand All @@ -2050,7 +2050,7 @@ These headers must be present in each request.
"rss": 185312,
"native_total_heap": 24828,
"native_free_heap": 1452,
"interval_config": 2000,
"interval": 2000,
"timestamp": "2024-05-03T23:34:21.565Z"
},
{
Expand All @@ -2061,7 +2061,7 @@ These headers must be present in each request.
"rss": 185920,
"native_total_heap": 24828,
"native_free_heap": 1452,
"interval_config": 2000,
"interval": 2000,
"timestamp": "2024-05-03T23:34:23.571Z"
},
{
Expand All @@ -2072,7 +2072,7 @@ These headers must be present in each request.
"rss": 185920,
"native_total_heap": 24828,
"native_free_heap": 1436,
"interval_config": 2000,
"interval": 2000,
"timestamp": "2024-05-03T23:34:25.568Z"
},
{
Expand All @@ -2083,7 +2083,7 @@ These headers must be present in each request.
"rss": 191340,
"native_total_heap": 26620,
"native_free_heap": 2879,
"interval_config": 2000,
"interval": 2000,
"timestamp": "2024-05-03T23:34:27.565Z"
}
],
Expand Down
6 changes: 3 additions & 3 deletions docs/api/sdk/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -613,7 +613,8 @@ Use the `cpu_usage` type for CPU usage of a Linux based OS.
| `stime` | number | No | Time spent executing code in kernel mode, in Jiffies. |
| `cutime` | number | No | Time spent executing code in user mode with children, in Jiffies. |
| `cstime` | number | No | Time spent executing code in kernel mode with children, in Jiffies. |
| `interval_config` | number | No | The interval between two collections, in ms. |
| `interval` | number | No | The interval between two collections, in ms. |
| `percentage_usage`| number | No | The percentage CPU usage in the interval. |
| `start_time` | number | No | The process start time, in Jiffies. |

#### **`memory_usage`**
Expand All @@ -629,7 +630,7 @@ Use the `memory_usage` type for memory usage of JVM applications.
| rss | number | Yes | Resident set size of the Java process - the amount of physical memory currently used by the Java application. Measured in kB. |
| native_total_heap | number | No | Total size of the native heap (memory outside of Java's control) available for memory allocation. Measured in kB. |
| native_free_heap | number | No | Amount of free memory available in the native heap. Measured in kB. |
| interval_config | number | No | The interval between two consecutive readings. Measured in ms. |
| interval | number | No | The interval between two consecutive readings. Measured in ms. |

#### **`low_memory`**

Expand All @@ -644,7 +645,6 @@ Use the `low_memory` type for a low memory event from the system.
| rss | number | Yes | Resident set size of the Java process - the amount of physical memory currently used by the Java application. Measured in kB. |
| native_total_heap | number | No | Total size of the native heap (memory outside of Java's control) available for memory allocation. Measured in kB. |
| native_free_heap | number | No | Amount of free memory available in the native heap. Measured in kB. |
| interval_config | number | No | The interval between two consecutive readings. Measured in ms. |

#### **`trim_memory`**

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package sh.measure.android.performance

internal fun calculatePercentageUsage(
utime: Long,
stime: Long,
cutime: Long,
cstime: Long,
uptime: Long,
previousUtime: Long,
previousStime: Long,
previousCutime: Long,
previousCstime: Long,
previousUptime: Long,
numCores: Int,
clockSpeedHz: Long,
): Double {
if (numCores <= 0) {
// This should never happen, but just in case.
return 0.0
}
val durationInSeconds = (uptime - previousUptime) / 1000
if (durationInSeconds <= 0) {
// This should never happen, but just in case.
return 0.0
}
val total = utime + stime + cutime + cstime
val previousTotal = previousUtime + previousStime + previousCutime + previousCstime
val deltaTotalUsage = total - previousTotal

val usageTimeInSeconds = deltaTotalUsage / clockSpeedHz.toDouble()
val usageOverInterval = usageTimeInSeconds / durationInSeconds
val averageUsagePerCore = usageOverInterval / numCores
val percentageUsage = averageUsagePerCore * 100
if (percentageUsage < 0) {
// This should never happen (utime, stime, cutime, cstime are cumulative values), but
// just in case.
return 0.0
}
return percentageUsage
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ internal class CpuUsageCollector(
private val procProvider: ProcProvider = ProcProviderImpl(),
private val osSysConfProvider: OsSysConfProvider = OsSysConfProviderImpl(),
) {
private var prevCpuUsageData: CpuUsageData? = null
@VisibleForTesting
var prevCpuUsageData: CpuUsageData? = null

@VisibleForTesting
var future: Future<*>? = null
Expand Down Expand Up @@ -60,6 +61,9 @@ internal class CpuUsageCollector(
val clockSpeedHz = osSysConfProvider.get(OsConstants._SC_CLK_TCK)
if (clockSpeedHz <= 0L || numCores <= 0L) return
val uptime = timeProvider.elapsedRealtime
val percentageCpuUsage =
getPercentageCpuUsage(utime, stime, cutime, cstime, uptime, numCores, clockSpeedHz)
val interval = getInterval(uptime)
val cpuUsageData = CpuUsageData(
num_cores = numCores,
clock_speed = clockSpeedHz,
Expand All @@ -69,9 +73,11 @@ internal class CpuUsageCollector(
cutime = cutime,
cstime = cstime,
start_time = startTime,
interval_config = CPU_TRACKING_INTERVAL_MS,
interval = interval,
percentage_usage = percentageCpuUsage,
)
if (prevCpuUsageData?.utime == cpuUsageData.utime && prevCpuUsageData?.stime == cpuUsageData.stime) {
if (prevCpuUsageData?.percentage_usage == cpuUsageData.percentage_usage) {
// do not track the event if the usage is the same as the previous one.
return
}
eventProcessor.track(
Expand All @@ -82,6 +88,41 @@ internal class CpuUsageCollector(
prevCpuUsageData = cpuUsageData
}

private fun getInterval(uptime: Long): Long {
return if (prevCpuUsageData != null) {
uptime - prevCpuUsageData!!.uptime
} else {
0
}
}

private fun getPercentageCpuUsage(
utime: Long,
stime: Long,
cutime: Long,
cstime: Long,
uptime: Long,
numCores: Int,
clockSpeedHz: Long,
) = if (prevCpuUsageData == null) {
0.0
} else {
calculatePercentageUsage(
utime = utime,
stime = stime,
cutime = cutime,
cstime = cstime,
uptime = uptime,
previousCstime = prevCpuUsageData!!.cstime,
previousCutime = prevCpuUsageData!!.cutime,
previousStime = prevCpuUsageData!!.stime,
previousUtime = prevCpuUsageData!!.utime,
previousUptime = prevCpuUsageData!!.uptime,
numCores = numCores,
clockSpeedHz = clockSpeedHz,
)
}

private fun readStatFile(): Array<Long>? {
val pid = processInfo.getPid()
val file = procProvider.getStatFile(pid)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,5 +39,9 @@ internal data class CpuUsageData(
/**
* The interval between two collections.
*/
val interval_config: Long,
val interval: Long,
/**
* Average %CPU usage in the interval set by [interval].
*/
val percentage_usage: Double,
)
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ internal data class MemoryUsageData(
val rss: Long?,
val native_total_heap: Long,
val native_free_heap: Long,
val interval_config: Long,
val interval: Long,
)

@Serializable
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,12 @@ internal class MemoryUsageCollector(
@VisibleForTesting
var future: Future<*>? = null

@VisibleForTesting
internal var previousMemoryUsage: MemoryUsageData? = null

@VisibleForTesting
internal var previousMemoryUsageReadTimeMs = 0L

fun register() {
if (!processInfo.isForegroundProcess()) return
if (future != null) return
Expand All @@ -45,19 +51,28 @@ internal class MemoryUsageCollector(
}

private fun trackMemoryUsage() {
val interval = if (previousMemoryUsageReadTimeMs != 0L) {
timeProvider.elapsedRealtime - previousMemoryUsageReadTimeMs
} else {
0
}
previousMemoryUsageReadTimeMs = timeProvider.elapsedRealtime

val data = MemoryUsageData(
java_max_heap = memoryReader.maxHeapSize(),
java_total_heap = memoryReader.totalHeapSize(),
java_free_heap = memoryReader.freeHeapSize(),
total_pss = memoryReader.totalPss(),
rss = memoryReader.rss(),
native_total_heap = memoryReader.nativeTotalHeapSize(),
native_free_heap = memoryReader.nativeFreeHeapSize(),
interval = interval,
)
eventProcessor.track(
timestamp = timeProvider.currentTimeSinceEpochInMillis,
type = EventType.MEMORY_USAGE,
data = MemoryUsageData(
java_max_heap = memoryReader.maxHeapSize(),
java_total_heap = memoryReader.totalHeapSize(),
java_free_heap = memoryReader.freeHeapSize(),
total_pss = memoryReader.totalPss(),
rss = memoryReader.rss(),
native_total_heap = memoryReader.nativeTotalHeapSize(),
native_free_heap = memoryReader.nativeFreeHeapSize(),
interval_config = MEMORY_TRACKING_INTERVAL_MS,
),
data = data,
)
previousMemoryUsage = data
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,7 @@ internal object FakeEventFactory {
rss: Long? = 500,
nativeTotalHeap: Long = 600,
nativeFreeHeap: Long = 700,
intervalConfig: Long = 800,
interval: Long = 800,
): MemoryUsageData {
return MemoryUsageData(
javaMaxHeap,
Expand All @@ -248,7 +248,7 @@ internal object FakeEventFactory {
rss,
nativeTotalHeap,
nativeFreeHeap,
intervalConfig,
interval,
)
}

Expand Down Expand Up @@ -295,7 +295,8 @@ internal object FakeEventFactory {
cutime: Long = 9876L,
cstime: Long = 1234L,
stime: Long = 9876L,
intervalConfig: Long = 1000,
interval: Long = 1000,
percentageUsage: Double = 0.0,
): CpuUsageData {
return CpuUsageData(
numCores,
Expand All @@ -306,7 +307,8 @@ internal object FakeEventFactory {
cutime,
cstime,
stime,
intervalConfig,
interval,
percentageUsage,
)
}

Expand Down
Loading
Loading