Skip to content

Commit

Permalink
Memfault BORT SDK 5.1.0 (Build 2456766)
Browse files Browse the repository at this point in the history
  • Loading branch information
Memfault Inc. committed Sep 14, 2024
1 parent 54aeef8 commit 138e15b
Show file tree
Hide file tree
Showing 7 changed files with 126 additions and 26 deletions.
17 changes: 17 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,22 @@
# Memfault Bort Changelog

## v5.1.0 - September 13, 2024

### :construction: Fixes

- Fixed Stability Device Vitals: a change to the way metrics are collected in
Bort 4.17.0 meant that `operational_hours` would erroneously be set to zero,
resulting in incorrect Stability Vital charts.

### :chart_with_upwards_trend: Improvements

- Added new screen on/off battery drain metrics:
`battery_screen_on_discharge_duration_ms`, `battery_screen_on_soc_pct_drop`,
`battery_screen_off_discharge_duration_ms`, `battery_screen_off_soc_pct_drop`.
These will replace `screen_off_battery_drain_%/hour`/
`screen_on_battery_drain_%/hour` in the future, once they are supported in the
Memfault dashboard (to more accurately track battery drain across the fleet).

## v5.0.0 - September 12, 2024

### :boom: Breaking Changes
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.memfault.bort.metrics

import com.memfault.bort.TemporaryFileFactory
import com.memfault.bort.metrics.BatterystatsSummaryCollector.Companion.DP
import com.memfault.bort.metrics.HighResTelemetry.DataType.DoubleType
import com.memfault.bort.metrics.HighResTelemetry.Datum
import com.memfault.bort.metrics.HighResTelemetry.MetricType
Expand Down Expand Up @@ -102,15 +103,25 @@ class BatterystatsSummaryCollector @Inject constructor(
hrt.add(rollup)
}

// Screen off drain
// Screen off drain: old metric (to be deleted once we are using the new one, at some point)
if (diff.screenOffRealtimeMs > 0 && diff.screenOffDrainPercent != null) {
val screenOffBatteryDrainPerHour =
JsonPrimitive(diff.screenOffDrainPercent.proRataValuePerHour(diff.screenOffRealtimeMs.milliseconds))
addHrtRollup(name = SCREEN_OFF_BATTERY_DRAIN_PER_HOUR, value = screenOffBatteryDrainPerHour)
report[SCREEN_OFF_BATTERY_DRAIN_PER_HOUR] = screenOffBatteryDrainPerHour
}

// Screen on drain
// Screen off drain: new metrics
if (diff.screenOffDrainPercent != null) {
val screenOffDischargeDurationMs = JsonPrimitive(diff.screenOffRealtimeMs)
addHrtRollup(name = SCREEN_OFF_DISCHARGE_DURATION_MS, value = screenOffDischargeDurationMs)
report[SCREEN_OFF_DISCHARGE_DURATION_MS] = screenOffDischargeDurationMs
val screenOffPercentDrop = JsonPrimitive(diff.screenOffDrainPercent.roundTo(DP))
addHrtRollup(name = SCREEN_OFF_SOC_PCT_DROP, value = screenOffPercentDrop)
report[SCREEN_OFF_SOC_PCT_DROP] = screenOffPercentDrop
}

// Screen on drain: old metric (to be deleted once we are using the new one, at some point)
val screenOnRealtimeMs = diff.batteryRealtimeMs - diff.screenOffRealtimeMs
if (screenOnRealtimeMs > 0 && diff.screenOnDrainPercent != null) {
val screenOnBatteryDrainPerHour =
Expand All @@ -119,6 +130,16 @@ class BatterystatsSummaryCollector @Inject constructor(
report[SCREEN_ON_BATTERY_DRAIN_PER_HOUR] = screenOnBatteryDrainPerHour
}

// Screen on drain: new metrics
if (diff.screenOnDrainPercent != null) {
val screenOnDischargeDurationMs = JsonPrimitive(screenOnRealtimeMs)
addHrtRollup(name = SCREEN_ON_DISCHARGE_DURATION_MS, value = screenOnDischargeDurationMs)
report[SCREEN_ON_DISCHARGE_DURATION_MS] = screenOnDischargeDurationMs
val screenOnPercentDrop = JsonPrimitive(diff.screenOnDrainPercent.roundTo(DP))
addHrtRollup(name = SCREEN_ON_SOC_PCT_DROP, value = screenOnPercentDrop)
report[SCREEN_ON_SOC_PCT_DROP] = screenOnPercentDrop
}

if (summary.batteryState.estimatedBatteryCapacity > 0) {
val estimatedCapacityMah = JsonPrimitive(summary.batteryState.estimatedBatteryCapacity)
addHrtRollup(name = ESTIMATED_BATTERY_CAPACITY, value = estimatedCapacityMah)
Expand Down Expand Up @@ -218,6 +239,11 @@ class BatterystatsSummaryCollector @Inject constructor(
const val MIN_BATTERY_CAPACITY = "min_battery_capacity_mah"
const val MAX_BATTERY_CAPACITY = "max_battery_capacity_mah"
const val BATTERY_STATE_OF_HEALTH = "battery_state_of_health_%"
const val SCREEN_ON_DISCHARGE_DURATION_MS = "battery_screen_on_discharge_duration_ms"
const val SCREEN_ON_SOC_PCT_DROP = "battery_screen_on_soc_pct_drop"
const val SCREEN_OFF_DISCHARGE_DURATION_MS = "battery_screen_off_discharge_duration_ms"
const val SCREEN_OFF_SOC_PCT_DROP = "battery_screen_off_soc_pct_drop"
const val DP = 2
}
}

Expand Down Expand Up @@ -310,7 +336,7 @@ private operator fun PowerUseSummary.minus(other: PowerUseSummary) = PowerUseSum
maxCapacityMah = maxCapacityMah - other.maxCapacityMah,
)

private fun Double.proRataValuePerHour(period: Duration, dp: Int = 2) =
private fun Double.proRataValuePerHour(period: Duration, dp: Int = DP) =
((this / period.inWholeMilliseconds.toDouble()) * 1.hours.inWholeMilliseconds.toDouble()).roundTo(dp)

fun Double.roundTo(n: Int): Double {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,12 +46,12 @@ data class CrashFreeHoursState(
)

class CrashFreeHoursMetricLogger @Inject constructor() {
fun incrementOperationalHours(hours: Long) {
OPERATIONAL_HOURS_METRIC.increment(hours)
fun incrementOperationalHours(hours: Int) {
OPERATIONAL_HOURS_METRIC.incrementBy(by = hours)
}

fun incrementCrashFreeHours(hours: Long) {
CRASH_FREE_HOURS_METRIC.increment(hours)
fun incrementCrashFreeHours(hours: Int) {
CRASH_FREE_HOURS_METRIC.incrementBy(by = hours)
}

companion object {
Expand Down Expand Up @@ -95,7 +95,7 @@ class CrashFreeHours @Inject constructor(
override fun process() {
val now = timeProvider.now().elapsedRealtime.duration
val elapsed = now - storage.state.hourStartedAtElapsedRealtimeMs.toDuration(MILLISECONDS)
val elapsedHours = elapsed.inWholeHours
val elapsedHours = elapsed.inWholeHours.toInt()

if (elapsedHours > 0) {
metricLogger.incrementOperationalHours(elapsedHours)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ class MetricsCollectionTask @Inject constructor(
override fun convertAndValidateInputData(inputData: Data) = Unit

private suspend fun enqueueHeartbeatUpload(
collectionTime: CombinedTime,
initialCollectionTime: CombinedTime,
lastHeartbeatUptime: BaseLinuxBootRelativeTime,
propertiesStore: DevicePropertiesStore,
) {
Expand All @@ -163,16 +163,16 @@ class MetricsCollectionTask @Inject constructor(
val softwareVersionChanged = customMetrics.softwareVersionChanged(deviceSoftwareVersion)

val batteryStatsResult = batteryStatsCollector.collect(
collectionTime = collectionTime,
collectionTime = initialCollectionTime,
lastHeartbeatUptime = lastHeartbeatUptime,
)
Logger.test("Metrics: properties_use_service = ${metricsSettings.propertiesUseMetricService}")
Logger.test("Metrics: software version = $heartbeatSoftwareVersion -> $deviceSoftwareVersion")

// These write to Custom Metrics - do before finishing the heartbeat report.
storageStatsCollector.collectStorageStats(collectionTime)
storageStatsCollector.collectStorageStats(initialCollectionTime)
networkStatsCollector.collectAndRecord(
collectionTime = collectionTime,
collectionTime = initialCollectionTime,
lastHeartbeatUptime = lastHeartbeatUptime,
)

Expand Down Expand Up @@ -224,13 +224,16 @@ class MetricsCollectionTask @Inject constructor(
val inMemoryMetrics = listOf(
appStorageStatsCollector,
databaseSizeCollector,
).flatMap { collector -> collector.collect(collectionTime) }
).flatMap { collector -> collector.collect(initialCollectionTime) }
val inMemoryHeartbeats = inMemoryMetrics.heartbeatMetrics()
val inMemoryInternalHeartbeats = inMemoryMetrics.internalHeartbeatMetrics()
val inMemoryHrtRollups = inMemoryMetrics.hrtRollups(collectionTime)
val inMemoryHrtRollups = inMemoryMetrics.hrtRollups(initialCollectionTime)

// Ensure that we set the "actual" collection time after all metrics have been collected, so that they will all
// be included in the report.
val actualCollectionTime = combinedTimeProvider.now()
val heartbeatReport = customMetrics.collectHeartbeat(
endTimestampMs = collectionTime.timestamp.toEpochMilli(),
endTimestampMs = actualCollectionTime.timestamp.toEpochMilli(),
forceEndAllReports = softwareVersionChanged,
)

Expand All @@ -243,7 +246,7 @@ class MetricsCollectionTask @Inject constructor(
.takeIf { it.isPositive() }

// This duration can also be negative, but it's the same as we had before.
val hourlyHeartbeatDurationFromUptime = collectionTime.elapsedRealtime.duration -
val hourlyHeartbeatDurationFromUptime = actualCollectionTime.elapsedRealtime.duration -
lastHeartbeatUptime.elapsedRealtime.duration

val hourlyHeartbeatDuration = hourlyHeartbeatDurationFromReport
Expand All @@ -253,15 +256,15 @@ class MetricsCollectionTask @Inject constructor(
val heartbeatReportInternalMetrics = hourlyHeartbeatReport.internalMetrics

clientRateLimitCollector.collect(
collectionTime = collectionTime,
collectionTime = actualCollectionTime,
internalHeartbeatReportMetrics = heartbeatReportInternalMetrics,
)

// If there were no heartbeat internal metrics, then fallback to include some core values.
val internalMetrics = heartbeatReportInternalMetrics.ifEmpty { fallbackInternalMetrics }
uploadHeartbeat(
batteryStatsFile = batteryStatsResult.batteryStatsFileToUpload,
collectionTime = collectionTime,
collectionTime = actualCollectionTime,
heartbeatInterval = hourlyHeartbeatDuration,
heartbeatReportMetrics = heartbeatReportMetrics +
batteryStatsResult.aggregatedMetrics +
Expand All @@ -280,7 +283,7 @@ class MetricsCollectionTask @Inject constructor(
hourlyHeartbeatReport.hrt?.let { hrtFile ->
val hrtMetricsToAdd = batteryStatsResult.batteryStatsHrt +
inMemoryHrtRollups +
propertiesStore.hrtRollups(timestampMs = collectionTime.timestamp.toEpochMilli())
propertiesStore.hrtRollups(timestampMs = actualCollectionTime.timestamp.toEpochMilli())
if (hrtMetricsToAdd.isNotEmpty()) {
mergeHrtIntoFile(hrtFile, hrtMetricsToAdd)
}
Expand All @@ -295,7 +298,7 @@ class MetricsCollectionTask @Inject constructor(
heartbeatReport.dailyHeartbeatReport?.let { report ->
uploadHeartbeat(
batteryStatsFile = null,
collectionTime = collectionTime,
collectionTime = actualCollectionTime,
heartbeatInterval = (report.endTimestampMs - report.startTimestampMs)
.toDuration(MILLISECONDS),
heartbeatReportMetrics = report.metrics +
Expand All @@ -315,7 +318,7 @@ class MetricsCollectionTask @Inject constructor(
.forEach { session ->
uploadHeartbeat(
batteryStatsFile = null,
collectionTime = collectionTime,
collectionTime = actualCollectionTime,
heartbeatInterval = (session.endTimestampMs - session.startTimestampMs).toDuration(MILLISECONDS),
heartbeatReportMetrics = session.metrics,
heartbeatReportInternalMetrics = session.internalMetrics,
Expand Down
Loading

0 comments on commit 138e15b

Please sign in to comment.