mirror of
git://nv-tegra.nvidia.com/linux-hwpm.git
synced 2025-12-22 09:12:05 +03:00
Add mode E user data test for these IPS: - NVTHERM - IPMU JIRA MSST-831 Change-Id: Id8911fa9bbed47f1c5d1e82b075e60134e05ad2c Signed-off-by: Besar Wicaksono <bwicaksono@nvidia.com> Reviewed-on: https://git-master.nvidia.com/r/c/linux-hwpm/+/3361434 Reviewed-by: Vasuki Shankar <vasukis@nvidia.com> GVS: buildbot_gerritrpt <buildbot_gerritrpt@nvidia.com> Reviewed-by: Yifei Wan <ywan@nvidia.com>
1012 lines
33 KiB
C++
1012 lines
33 KiB
C++
/* SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
|
* SPDX-License-Identifier: GPL-2.0-only
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
* under the terms and conditions of the GNU General Public License,
|
|
* version 2, as published by the Free Software Foundation.
|
|
*
|
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
* more details.
|
|
*
|
|
*/
|
|
|
|
#include "soc_mode_e_buffer.h"
|
|
#include "hwpm_record_format.h"
|
|
|
|
#include <stdio.h>
|
|
#include <inttypes.h>
|
|
#include <unistd.h>
|
|
#include <iostream>
|
|
#include <cstring>
|
|
#include <deque>
|
|
|
|
SocModeEBuffer::SocModeEBuffer(nv_soc_hwpm_api_table& api_table, nv_soc_hwpm_session session)
|
|
: m_api_table(api_table)
|
|
, m_session(session)
|
|
, m_record_format(RecordFormatType::ModeE)
|
|
, m_num_valid_records(0)
|
|
, m_num_overflow_records(0)
|
|
, m_num_pma_records(0)
|
|
, m_num_samples(0)
|
|
, m_delayed_sample_detected(false)
|
|
, m_merged_samples_detected(false)
|
|
, m_sum_counter_values(0)
|
|
, m_zero_timestamp_detected(false)
|
|
, m_reversed_trigger_count_detected(false)
|
|
, m_local_trigger_bookmark_mismatch(false)
|
|
, m_first_pma_timestamp(0)
|
|
, m_last_pma_timestamp(0)
|
|
, m_first_sys0_timestamp(0)
|
|
, m_last_sys0_timestamp(0)
|
|
, m_pma_buffer(nullptr)
|
|
, m_membytes_buffer(nullptr)
|
|
, m_max_records(0)
|
|
, m_unread_head(0)
|
|
, m_pma_buffer_size(0)
|
|
, m_pma_buffer_cpu_va(nullptr)
|
|
, m_mem_bytes_buffer_cpu_va(nullptr)
|
|
{
|
|
}
|
|
|
|
SocModeEBuffer::~SocModeEBuffer()
|
|
{
|
|
}
|
|
|
|
bool SocModeEBuffer::Initialize()
|
|
{
|
|
nv_soc_hwpm_session_attribute session_attr;
|
|
|
|
session_attr = NV_SOC_HWPM_SESSION_ATTRIBUTE_PMA_RECORD_BUFFER_SIZE;
|
|
if (m_api_table.nv_soc_hwpm_session_get_info_fn(
|
|
m_session,
|
|
session_attr,
|
|
sizeof(m_pma_buffer_size),
|
|
&m_pma_buffer_size)) {
|
|
printf("ERROR: SOC HWPM session get info buffer size failed!\n");
|
|
return false;
|
|
}
|
|
|
|
session_attr = NV_SOC_HWPM_SESSION_ATTRIBUTE_PMA_RECORD_BUFFER_CPU_VA;
|
|
if (m_api_table.nv_soc_hwpm_session_get_info_fn(
|
|
m_session,
|
|
session_attr,
|
|
sizeof(m_pma_buffer_cpu_va),
|
|
&m_pma_buffer_cpu_va)) {
|
|
printf("ERROR: SOC HWPM session get info stream buffer ptr failed!\n");
|
|
return false;
|
|
}
|
|
|
|
session_attr = NV_SOC_HWPM_SESSION_ATTRIBUTE_PMA_MEM_BYTES_BUFFER_CPU_VA;
|
|
if (m_api_table.nv_soc_hwpm_session_get_info_fn(
|
|
m_session,
|
|
session_attr,
|
|
sizeof(m_mem_bytes_buffer_cpu_va),
|
|
&m_mem_bytes_buffer_cpu_va)) {
|
|
printf("ERROR: SOC HWPM session get info mem bytes buffer ptr failed!\n");
|
|
return false;
|
|
}
|
|
|
|
m_max_records = m_pma_buffer_size / sizeof(ModeERecordRaw);
|
|
|
|
return true;
|
|
}
|
|
|
|
void SocModeEBuffer::ResetParsedData()
|
|
{
|
|
m_num_valid_records = 0;
|
|
m_num_overflow_records = 0;
|
|
m_num_pma_records = 0;
|
|
m_num_samples = 0;
|
|
m_delayed_sample_detected = false;
|
|
m_merged_samples_detected = false;
|
|
m_sum_counter_values = 0;
|
|
m_num_local_triggers = 0;
|
|
m_zero_timestamp_detected = false;
|
|
m_reversed_trigger_count_detected = false;
|
|
m_local_trigger_bookmark_mismatch = false;
|
|
m_perfmon_id_trigger_count_map.clear();
|
|
|
|
m_first_pma_timestamp = 0;
|
|
m_last_pma_timestamp = 0;
|
|
m_first_sys0_timestamp = 0;
|
|
m_last_sys0_timestamp = 0;
|
|
}
|
|
|
|
void SocModeEBuffer::SetRecordFormat(const RecordFormatType record_format_type)
|
|
{
|
|
m_record_format = record_format_type;
|
|
}
|
|
|
|
// Copied from Perfkit\Shared\Perfkit\Tests\Emulation\SOC\Tests\SocCrossTriggerTest\Src\SocModeEBuffer.cpp
|
|
#define LOCAL_TRIGGER_BOOKMARK_VAL 0xBEEF
|
|
void SocModeEBuffer::ParseRecords()
|
|
{
|
|
ResetParsedData();
|
|
|
|
// init buffer parsing variables
|
|
uint32_t record_idx_lo = m_unread_head;
|
|
uint32_t record_idx_hi = m_max_records;
|
|
bool valid_record_end_detected = false;
|
|
|
|
// Buffer wraparound requires two iterations
|
|
for (uint32_t circular_buffer_segment = 0; circular_buffer_segment < 2;
|
|
circular_buffer_segment++) {
|
|
// Iterate records in buffer
|
|
for (uint32_t ii = record_idx_lo; ii < record_idx_hi; ii++) {
|
|
auto p_record = (ModeERecordRaw*)(m_pma_buffer_cpu_va) + ii;
|
|
auto p_record_common_prefix = (PmRecordSocCommonPrefix*)(p_record);
|
|
uint32_t record_perfmon_id = p_record_common_prefix->GetPerfmonId();
|
|
|
|
if (record_perfmon_id == PMA_PerfmonId) { // PMA Record
|
|
const PmaRecordSoc* p_pma_record =
|
|
(const PmaRecordSoc*)(p_record_common_prefix);
|
|
const uint64_t timestamp = p_pma_record->GetPtimer();
|
|
if (!m_first_pma_timestamp) {
|
|
m_first_pma_timestamp = timestamp;
|
|
}
|
|
m_last_pma_timestamp = timestamp;
|
|
m_num_pma_records++;
|
|
} else if (record_perfmon_id) { // Mode C/E Record
|
|
if (m_record_format == RecordFormatType::ModeC) {
|
|
auto p_mode_c_record = (ModeCRecordVolta_12x16*)p_record;
|
|
uint16_t sample_count = p_mode_c_record->GetSampleCount();
|
|
bool delayed_sampled = p_mode_c_record->IsDelayedSampled();
|
|
|
|
// Mode C record does not have TOTAL_TRIG_RCV
|
|
if (!sample_count && !delayed_sampled) {
|
|
m_num_overflow_records++;
|
|
}
|
|
|
|
m_num_valid_records++;
|
|
m_num_samples += sample_count;
|
|
|
|
if (sample_count > 1) {
|
|
m_merged_samples_detected = true;
|
|
}
|
|
if (delayed_sampled) {
|
|
m_delayed_sample_detected = true;
|
|
}
|
|
for (uint32_t counter_idx = 0; counter_idx < 12;
|
|
counter_idx++) {
|
|
m_sum_counter_values += p_mode_c_record->counter[counter_idx];
|
|
}
|
|
|
|
// Mode C record does not have TOTAL_TRIG_RCV
|
|
m_perfmon_id_trigger_count_map.emplace(record_perfmon_id, 0);
|
|
|
|
// Local trigger field checks
|
|
const uint16_t local_trigger_count =
|
|
p_mode_c_record->counter[10];
|
|
m_num_local_triggers += local_trigger_count;
|
|
|
|
const uint16_t local_trigger_bookmark =
|
|
p_mode_c_record->counter[11];
|
|
if (local_trigger_bookmark != LOCAL_TRIGGER_BOOKMARK_VAL) {
|
|
m_local_trigger_bookmark_mismatch = true;
|
|
}
|
|
|
|
// TODO: Timestamp monotonic increment check
|
|
if (!p_mode_c_record->GetTimestamp()) {
|
|
m_zero_timestamp_detected = true;
|
|
}
|
|
} else if (m_record_format == RecordFormatType::ModeE) {
|
|
auto p_mode_e_record = (ModeERecordVolta*)p_record;
|
|
uint16_t sample_count = p_mode_e_record->GetSampleCount();
|
|
bool delayed_sampled = p_mode_e_record->IsDelayedSampled();
|
|
const uint32_t total_trigger_count = p_mode_e_record->zero2;
|
|
auto map_entry = m_perfmon_id_trigger_count_map.find(
|
|
record_perfmon_id);
|
|
|
|
// sys0 timestamp
|
|
if (record_perfmon_id == 0x70B) { // FIXME: halify!!!
|
|
uint64_t timestamp = p_mode_e_record->GetTimestamp();
|
|
if (!m_first_sys0_timestamp) {
|
|
m_first_sys0_timestamp = timestamp;
|
|
}
|
|
m_last_sys0_timestamp = timestamp;
|
|
}
|
|
|
|
if (!sample_count && !delayed_sampled &&
|
|
(map_entry == m_perfmon_id_trigger_count_map.end() ||
|
|
map_entry->second == total_trigger_count)) {
|
|
m_num_overflow_records++;
|
|
}
|
|
|
|
m_num_valid_records++;
|
|
m_num_samples += sample_count;
|
|
|
|
if (sample_count > 1) {
|
|
m_merged_samples_detected = true;
|
|
}
|
|
if (delayed_sampled) {
|
|
m_delayed_sample_detected = true;
|
|
}
|
|
m_sum_counter_values += p_mode_e_record->counter[0] +
|
|
p_mode_e_record->counter[1] +
|
|
p_mode_e_record->counter[2] +
|
|
p_mode_e_record->counter[3];
|
|
|
|
// TOTAL_TRIG_RCV monotonic check
|
|
if (map_entry != m_perfmon_id_trigger_count_map.end()) {
|
|
if (map_entry->second >= total_trigger_count) {
|
|
m_reversed_trigger_count_detected = true;
|
|
}
|
|
map_entry->second = total_trigger_count;
|
|
} else {
|
|
m_perfmon_id_trigger_count_map.emplace(record_perfmon_id,
|
|
total_trigger_count);
|
|
}
|
|
|
|
// Local trigger field checks
|
|
const uint16_t local_trigger_count =
|
|
p_mode_e_record->zero3 & 0xFFFF;
|
|
m_num_local_triggers += local_trigger_count;
|
|
|
|
const uint16_t local_trigger_bookmark =
|
|
p_mode_e_record->zero3 >> 16;
|
|
if (local_trigger_bookmark != LOCAL_TRIGGER_BOOKMARK_VAL) {
|
|
m_local_trigger_bookmark_mismatch = true;
|
|
}
|
|
|
|
// TODO: Timestamp monotonic increment check
|
|
if (!p_mode_e_record->GetTimestamp()) {
|
|
m_zero_timestamp_detected = true;
|
|
}
|
|
} else if (m_record_format == RecordFormatType::ModeE_userdata) {
|
|
m_num_valid_records++;
|
|
m_perfmon_id_trigger_count_map.emplace(record_perfmon_id, 0);
|
|
}
|
|
} else {
|
|
// Reach the end of valid records
|
|
valid_record_end_detected = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (valid_record_end_detected) {
|
|
// Early escape when buffer wraparound does not occur
|
|
break;
|
|
} else {
|
|
// Buffer wraparound detected! Alter buffer segment bounds
|
|
record_idx_lo = 0;
|
|
record_idx_hi = m_unread_head;
|
|
}
|
|
}
|
|
}
|
|
|
|
uint32_t SocModeEBuffer::GetNumValidRecords()
|
|
{
|
|
ParseRecords();
|
|
return m_num_valid_records;
|
|
}
|
|
|
|
uint32_t SocModeEBuffer::GetNumOverflowRecords()
|
|
{
|
|
ParseRecords();
|
|
return m_num_overflow_records;
|
|
}
|
|
|
|
uint32_t SocModeEBuffer::GetNumPmaRecords()
|
|
{
|
|
ParseRecords();
|
|
return m_num_pma_records;
|
|
}
|
|
|
|
uint32_t SocModeEBuffer::GetNumSamples()
|
|
{
|
|
ParseRecords();
|
|
return m_num_samples;
|
|
}
|
|
|
|
bool SocModeEBuffer::IsDelayedSampleDetected()
|
|
{
|
|
ParseRecords();
|
|
return m_delayed_sample_detected;
|
|
}
|
|
|
|
bool SocModeEBuffer::IsMergedSamplesDetected()
|
|
{
|
|
ParseRecords();
|
|
return m_merged_samples_detected;
|
|
}
|
|
|
|
uint64_t SocModeEBuffer::GetCounterValueSum()
|
|
{
|
|
ParseRecords();
|
|
return m_sum_counter_values;
|
|
}
|
|
|
|
uint32_t SocModeEBuffer::GetNumUniquePerfmonID()
|
|
{
|
|
ParseRecords();
|
|
return m_perfmon_id_trigger_count_map.size();
|
|
}
|
|
|
|
uint32_t SocModeEBuffer::GetNumLocalTriggers()
|
|
{
|
|
ParseRecords();
|
|
return m_num_local_triggers;
|
|
}
|
|
|
|
bool SocModeEBuffer::IsZeroTimestampDetected()
|
|
{
|
|
ParseRecords();
|
|
return m_zero_timestamp_detected;
|
|
}
|
|
|
|
bool SocModeEBuffer::IsReversedTriggerCountDetected()
|
|
{
|
|
ParseRecords();
|
|
return m_reversed_trigger_count_detected;
|
|
}
|
|
|
|
bool SocModeEBuffer::IsLocalTriggerBookmarkMismatchDetected()
|
|
{
|
|
ParseRecords();
|
|
return m_local_trigger_bookmark_mismatch;
|
|
}
|
|
|
|
uint32_t SocModeEBuffer::GetMemBytes()
|
|
{
|
|
// Emulation: Wait for in-flight mem_bytes to arrive.
|
|
usleep(10000);
|
|
|
|
auto* p_mem_bytes_addr = (uint32_t*)(m_mem_bytes_buffer_cpu_va);
|
|
return *p_mem_bytes_addr;
|
|
}
|
|
|
|
uint64_t SocModeEBuffer::GetPmaRecordElapsedTime()
|
|
{
|
|
ParseRecords();
|
|
return (m_last_pma_timestamp - m_first_pma_timestamp);
|
|
}
|
|
|
|
uint64_t SocModeEBuffer::GetSysRecordElapsedCycles()
|
|
{
|
|
ParseRecords();
|
|
return (m_last_sys0_timestamp - m_first_sys0_timestamp);
|
|
}
|
|
|
|
uint64_t SocModeEBuffer::GetFirstPmaTimestamp()
|
|
{
|
|
ParseRecords();
|
|
return m_first_pma_timestamp;
|
|
}
|
|
|
|
uint64_t SocModeEBuffer::GetLastPmaTimestamp()
|
|
{
|
|
ParseRecords();
|
|
return m_last_pma_timestamp;
|
|
}
|
|
|
|
void SocModeEBuffer::PrintRecord(
|
|
PmRecordSocCommonPrefix* record,
|
|
bool is_pma_record,
|
|
enum RecordFormatType format_type)
|
|
{
|
|
char str_buffer[256];
|
|
|
|
bool is_mode_c = format_type == RecordFormatType::ModeC;
|
|
bool is_mode_e = format_type == RecordFormatType::ModeE;
|
|
bool is_mode_e_userdata = format_type == RecordFormatType::ModeE_userdata;
|
|
|
|
if (is_pma_record)
|
|
{
|
|
auto p_pma_record = (PmaRecordSoc*)(record);
|
|
sprintf(str_buffer,
|
|
"[ PMA ] PTIMER %9" PRIu64 ", DR %d, TRIG %d, START %d, END %d, BKMRK %d\n",
|
|
p_pma_record->GetPtimer(),
|
|
p_pma_record->IsDroppedRecord(),
|
|
p_pma_record->GetTotalTriggerCount(),
|
|
p_pma_record->GetStartTriggerCount(),
|
|
p_pma_record->GetStopTriggerCount(),
|
|
p_pma_record->GetBookmark()
|
|
);
|
|
}
|
|
else if (is_mode_c)
|
|
{
|
|
auto p_mode_c_record = (ModeCRecordVolta_12x16*)(record);
|
|
sprintf(str_buffer,
|
|
"[MODEC] PERFMON %3x, TS %9" PRIu64 ", DS %d, SMPLCNT %d, C0 %d, C1 %d, C2 %d, C3 %d, C4 %d, C5 %d, C6 %d, C7 %d, C8 %d, C9 %d, C10 %d, C11 %d\n",
|
|
p_mode_c_record->GetPerfmonId(),
|
|
p_mode_c_record->GetTimestamp(),
|
|
p_mode_c_record->IsDelayedSampled(),
|
|
p_mode_c_record->GetSampleCount(),
|
|
p_mode_c_record->counter[0],
|
|
p_mode_c_record->counter[1],
|
|
p_mode_c_record->counter[2],
|
|
p_mode_c_record->counter[3],
|
|
p_mode_c_record->counter[4],
|
|
p_mode_c_record->counter[5],
|
|
p_mode_c_record->counter[6],
|
|
p_mode_c_record->counter[7],
|
|
p_mode_c_record->counter[8],
|
|
p_mode_c_record->counter[9],
|
|
p_mode_c_record->counter[10],
|
|
p_mode_c_record->counter[11]
|
|
);
|
|
}
|
|
else if (is_mode_e)
|
|
{
|
|
auto p_mode_e_record = (ModeERecordVolta*)(record);
|
|
sprintf(str_buffer,
|
|
"[MODEE] PERFMON %3x, TS %9" PRIu64 ", DS %d, SMPLCNT %d, EVENT %d, TRIG0 %d, TRIG1 %d, SAMPL %d, ZERO2 %x, ZERO3 %x\n",
|
|
p_mode_e_record->GetPerfmonId(),
|
|
p_mode_e_record->GetTimestamp(),
|
|
p_mode_e_record->IsDelayedSampled(),
|
|
p_mode_e_record->GetSampleCount(),
|
|
p_mode_e_record->event,
|
|
p_mode_e_record->trig0,
|
|
p_mode_e_record->trig1,
|
|
p_mode_e_record->sampl,
|
|
p_mode_e_record->zero2,
|
|
p_mode_e_record->zero3
|
|
);
|
|
}
|
|
else if (is_mode_e_userdata)
|
|
{
|
|
auto p_mode_e_userdata_record = (ModeERecordUserData*)(record);
|
|
sprintf(str_buffer,
|
|
"[MODEE_UD] PERFMON %3x, COUNT %4u, DROPPED %d, SD %d, TM %d, DATA 0-3 0x%x, DATA 4-7 0x%x, DATA 8-11 0x%x, DATA 12-15 0x%x, DATA 16-19 0x%x, DATA 20-23 0x%x, DATA 24-27 0x%x\n",
|
|
p_mode_e_userdata_record->GetPerfmonId(),
|
|
p_mode_e_userdata_record->GetCount(),
|
|
p_mode_e_userdata_record->GetDropped(),
|
|
p_mode_e_userdata_record->GetSD(),
|
|
p_mode_e_userdata_record->GetTM(),
|
|
p_mode_e_userdata_record->data0_3,
|
|
p_mode_e_userdata_record->data4_7,
|
|
p_mode_e_userdata_record->data8_11,
|
|
p_mode_e_userdata_record->data12_15,
|
|
p_mode_e_userdata_record->data16_19,
|
|
p_mode_e_userdata_record->data20_23,
|
|
p_mode_e_userdata_record->data24_27
|
|
);
|
|
}
|
|
std::cerr << str_buffer;
|
|
}
|
|
|
|
// This function parses the unread records in the PMA buffer and flushes them
|
|
// as a part of the KEEP_LATEST + buffer wraparound testing. Note that the
|
|
// parsing/flush operations are done in real time without stopping the PMA
|
|
// pulse and without doing MEM_BYTES and MEM_BUMP operations.
|
|
bool SocModeEBuffer::RealtimeParseFlush(SocRealtimeParseFlushData& stats, bool verbose)
|
|
{
|
|
// clear output stats
|
|
stats = {};
|
|
|
|
// initialize buffer parsing and bookkeeping variables
|
|
uint32_t record_idx_lo = m_unread_head;
|
|
uint32_t record_idx_hi = m_max_records;
|
|
bool valid_record_end_detected = false;
|
|
uint64_t last_pma_timestamp = 0;
|
|
uint32_t last_trigger_count = 0;
|
|
|
|
// PART 1: Parse unread records
|
|
// ============================
|
|
|
|
// Buffer wraparound requires two iterations
|
|
auto p_records = (ModeERecordRaw*)(m_pma_buffer_cpu_va); // FIXME: always Mode E?
|
|
for (uint32_t circular_buffer_segment = 0; circular_buffer_segment < 2; circular_buffer_segment++)
|
|
{
|
|
// Iterate records in buffer
|
|
for (uint32_t ii = record_idx_lo; ii < record_idx_hi; ii++)
|
|
{
|
|
auto p_record_common_prefix = (PmRecordSocCommonPrefix*)(p_records + ii);
|
|
uint32_t record_perfmon_id = p_record_common_prefix->GetPerfmonId();
|
|
|
|
if (record_perfmon_id)
|
|
{
|
|
if (record_perfmon_id == PMA_PerfmonId)
|
|
{
|
|
auto p_pma_record = (PmaRecordSoc*)p_record_common_prefix;
|
|
uint64_t curr_ptimer = p_pma_record->GetPtimer();
|
|
uint32_t total_trig_cnt = p_pma_record->GetTotalTriggerCount();
|
|
|
|
if (!curr_ptimer)
|
|
{
|
|
// Assume that complete record is not streamed
|
|
stats.m_incomplete_record_detected = true;
|
|
if (verbose)
|
|
{
|
|
std::cerr << "Incomplete PMA record: ptimer == 0\n";
|
|
PrintRecord(
|
|
p_record_common_prefix,
|
|
/*is_pma_record*/ true,
|
|
RecordFormatType::ModeC);
|
|
}
|
|
break;
|
|
}
|
|
else if (!total_trig_cnt)
|
|
{
|
|
// Assume that complete record is not streamed
|
|
stats.m_incomplete_record_detected = true;
|
|
if (verbose)
|
|
{
|
|
std::cerr << "Incomplete PMA record: totalTrigCnt == 0\n";
|
|
PrintRecord(
|
|
p_record_common_prefix,
|
|
/*is_pma_record*/ true,
|
|
RecordFormatType::ModeC);
|
|
}
|
|
break;
|
|
}
|
|
else if (curr_ptimer <= last_pma_timestamp)
|
|
{
|
|
stats.m_malformed_record = true;
|
|
if (verbose)
|
|
{
|
|
std::cerr << "Malformed PMA record: ptimer " << curr_ptimer << " <= lastPtimer " << last_pma_timestamp << "\n";
|
|
PrintRecord(
|
|
p_record_common_prefix,
|
|
/*is_pma_record*/ true,
|
|
RecordFormatType::ModeC);
|
|
}
|
|
break;
|
|
}
|
|
else if (total_trig_cnt <= last_trigger_count)
|
|
{
|
|
stats.m_malformed_record = true;
|
|
if (verbose)
|
|
{
|
|
std::cerr << "Malformed PMA record: totalTrigCnt " << (int)total_trig_cnt << " <= lastTriggerCount " << (int)last_trigger_count << "\n";
|
|
PrintRecord(p_record_common_prefix, /*is_pma_record*/true, RecordFormatType::ModeC);
|
|
}
|
|
break;
|
|
}
|
|
|
|
// Update Ptimer and trigger count for bookkeeping
|
|
last_pma_timestamp = curr_ptimer;
|
|
last_trigger_count = total_trig_cnt;
|
|
}
|
|
else
|
|
{
|
|
auto p_mode_e_record = (ModeERecordVolta*)p_record_common_prefix;
|
|
uint64_t timestamp = p_mode_e_record->GetTimestamp();
|
|
const uint32_t total_trigger_count = p_mode_e_record->zero2;
|
|
auto map_entry = m_perfmon_id_trigger_count_map.find(record_perfmon_id);
|
|
|
|
if (!timestamp)
|
|
{
|
|
// Assume that complete record is not streamed
|
|
stats.m_incomplete_record_detected = true;
|
|
if (verbose)
|
|
{
|
|
std::cerr << "Incomplete ModeE record: timestamp == 0\n";
|
|
PrintRecord(
|
|
p_record_common_prefix,
|
|
/*is_pma_record*/ false,
|
|
m_record_format);
|
|
}
|
|
break;
|
|
}
|
|
else if (!total_trigger_count)
|
|
{
|
|
// Assume that complete record is not streamed
|
|
stats.m_incomplete_record_detected = true;
|
|
if (verbose)
|
|
{
|
|
std::cerr << "Incomplete ModeE record: totalTriggerCount == 0\n";
|
|
PrintRecord(
|
|
p_record_common_prefix,
|
|
/*is_pma_record*/ false,
|
|
m_record_format);
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (map_entry != m_perfmon_id_trigger_count_map.end())
|
|
{
|
|
if (map_entry->second >= total_trigger_count)
|
|
{
|
|
stats.m_malformed_record = true;
|
|
if (verbose)
|
|
{
|
|
std::cerr << "Malformed ModeE record: totalTriggerCount " << (int)total_trigger_count << " <= lastTriggerCount " << (int)map_entry->second << "\n";
|
|
PrintRecord(
|
|
p_record_common_prefix,
|
|
/*is_pma_record*/ false,
|
|
m_record_format);
|
|
}
|
|
break;
|
|
}
|
|
map_entry->second = total_trigger_count;
|
|
}
|
|
else
|
|
{
|
|
m_perfmon_id_trigger_count_map.emplace(record_perfmon_id, total_trigger_count);
|
|
}
|
|
}
|
|
|
|
// If we reached here, we consumed one more record
|
|
stats.m_num_records_consumed++;
|
|
}
|
|
else
|
|
{
|
|
// Reach the end of valid records
|
|
valid_record_end_detected = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (stats.m_malformed_record || stats.m_incomplete_record_detected || valid_record_end_detected)
|
|
{
|
|
// Early escape when incomplete/malformed record is read, or buffer wraparound does not occur
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
// Buffer wraparound detected! Alter buffer segment bounds for the second parsing loop
|
|
stats.m_buffer_wraparound = true;
|
|
record_idx_lo = 0;
|
|
record_idx_hi = m_unread_head;
|
|
}
|
|
}
|
|
|
|
// PART 2: Flush
|
|
// =============
|
|
|
|
if (stats.m_malformed_record || !stats.m_num_records_consumed)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
uint32_t bytes_to_flush = stats.m_num_records_consumed * sizeof(ModeERecordRaw);
|
|
if (m_unread_head + stats.m_num_records_consumed <= m_max_records) // Single segment
|
|
{
|
|
memset(&p_records[m_unread_head], 0, bytes_to_flush);
|
|
// Move unread head back to 0 if unreadHead + numRecordsConsumed == maxRecords
|
|
m_unread_head = (m_unread_head + stats.m_num_records_consumed) % m_max_records;
|
|
}
|
|
else // Two segments
|
|
{
|
|
int num_records_first_segment = m_max_records - m_unread_head;
|
|
int num_records_second_segment = stats.m_num_records_consumed - num_records_first_segment;
|
|
memset(&p_records[m_unread_head], 0, sizeof(ModeERecordRaw) * num_records_first_segment);
|
|
memset(&p_records[0], 0, sizeof(ModeERecordRaw) * num_records_second_segment);
|
|
m_unread_head = num_records_second_segment;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool SocModeEBuffer::FlushRecordsInBuffer(const uint32_t bytes_to_flush)
|
|
{
|
|
// Empty flush
|
|
if (!bytes_to_flush) {
|
|
return true;
|
|
}
|
|
|
|
const uint32_t num_records_to_flush = bytes_to_flush / sizeof(ModeERecordRaw);
|
|
auto p_records = (ModeERecordRaw*)(m_pma_buffer_cpu_va);
|
|
if (m_unread_head + num_records_to_flush <= m_max_records) // Single segment
|
|
{
|
|
memset(&p_records[m_unread_head], 0, bytes_to_flush);
|
|
// Move unread head back to 0 if unreadHead + numRecordsToFlush == maxRecords
|
|
m_unread_head = (m_unread_head + num_records_to_flush) % m_max_records;
|
|
}
|
|
else // Two segments
|
|
{
|
|
int num_records_first_segment = m_max_records - m_unread_head;
|
|
int num_records_second_segment = num_records_to_flush - num_records_first_segment;
|
|
memset(&p_records[m_unread_head], 0, sizeof(ModeERecordRaw) * num_records_first_segment);
|
|
memset(&p_records[0], 0, sizeof(ModeERecordRaw) * num_records_second_segment);
|
|
m_unread_head = num_records_second_segment;
|
|
}
|
|
|
|
if (bytes_to_flush == m_pma_buffer_size) {
|
|
std::cout << "WARNING: Buffer is full! Reset PMA MEM_HEAD to guarantee HW-SW consistency...\n";
|
|
// TODO: reset PMA MEM_HEAD need regops
|
|
return false;
|
|
}
|
|
|
|
// Report flushed bytes to HW
|
|
nv_soc_hwpm_pma_channel_state_params param = {};
|
|
param.in_mem_bump = bytes_to_flush;
|
|
param.in_stream_mem_bytes = 0;
|
|
param.in_check_overflow = 0;
|
|
param.in_read_mem_head = 0;
|
|
if (m_api_table.nv_soc_hwpm_session_set_get_pma_state_fn(
|
|
m_session, ¶m)) {
|
|
std::cerr << "ERROR: SOC HWPM session set get pma state mem bump failed!\n";
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void SocModeEBuffer::PrintRecords(const size_t num_records_to_print) const
|
|
{
|
|
char str_buffer[256];
|
|
std::deque<std::string> record_strings;
|
|
if (m_record_format == RecordFormatType::ModeC)
|
|
{
|
|
printf("No. PerfmonID Elaps_cyc DS SmpCt C0 C1 C2 C3 C4 C5 C6 C7 C8 C9 C10 C11 \n");
|
|
printf("---- --------- --------- -- ----- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ----\n");
|
|
}
|
|
else if (m_record_format == RecordFormatType::ModeE)
|
|
{
|
|
printf("No. PerfmonID Elaps_cyc DS SmpCt Count0 Count1 Count2 Count3 TrgB TrgA \n");
|
|
printf("---- --------- --------- -- ----- ------- ------- ------- ------- -------- --------\n");
|
|
}
|
|
else if (m_record_format == RecordFormatType::ModeE_userdata)
|
|
{
|
|
printf("No. PerfmonID Count Dropped SD TM DATA0-3 DATA4-7 DATA8-11 DATA12-15 DATA16-19 DATA20-23 DATA24-27\n");
|
|
printf("---- --------- ----- ------- ---- -- ------- ------- -------- --------- --------- --------- ---------\n");
|
|
}
|
|
|
|
uint32_t record_idx_lo = m_unread_head;
|
|
uint32_t record_idx_hi = m_max_records;
|
|
bool valid_record_end_detected = false;
|
|
|
|
for (uint32_t circular_buffer_segment = 0; circular_buffer_segment < 2; circular_buffer_segment++)
|
|
{
|
|
// Iterate records in buffer
|
|
for (uint32_t ii = record_idx_lo; ii < record_idx_hi; ii++)
|
|
{
|
|
auto p_record = (ModeERecordRaw*)(m_pma_buffer_cpu_va) + ii;
|
|
auto p_record_common_prefix = (PmRecordSocCommonPrefix*)(p_record);
|
|
uint32_t perfmon_id = p_record_common_prefix->GetPerfmonId();
|
|
|
|
if (perfmon_id && perfmon_id != PMA_PerfmonId) // Mode C/E Record
|
|
{
|
|
if (m_record_format == RecordFormatType::ModeC)
|
|
{
|
|
auto p_mode_c_record = (ModeCRecordVolta_12x16*)p_record;
|
|
uint64_t timestamp = p_mode_c_record->GetTimestamp();
|
|
bool is_delayed_sampled = p_mode_c_record->IsDelayedSampled();
|
|
uint32_t sample_cnt = p_mode_c_record->GetSampleCount();
|
|
|
|
sprintf(str_buffer,
|
|
"%4d %9x %9" PRIu64 " %2x %5d %4x %4x %4x %4x %4x %4x %4x %4x %4x %4x %4x %4x\n",
|
|
ii,
|
|
perfmon_id,
|
|
timestamp,
|
|
is_delayed_sampled,
|
|
sample_cnt,
|
|
p_mode_c_record->counter[0],
|
|
p_mode_c_record->counter[1],
|
|
p_mode_c_record->counter[2],
|
|
p_mode_c_record->counter[3],
|
|
p_mode_c_record->counter[4],
|
|
p_mode_c_record->counter[5],
|
|
p_mode_c_record->counter[6],
|
|
p_mode_c_record->counter[7],
|
|
p_mode_c_record->counter[8],
|
|
p_mode_c_record->counter[9],
|
|
p_mode_c_record->counter[10],
|
|
p_mode_c_record->counter[11]
|
|
);
|
|
}
|
|
else if (m_record_format == RecordFormatType::ModeE)
|
|
{
|
|
auto p_mode_e_record = (ModeERecordVolta*)p_record;
|
|
uint64_t timestamp = p_mode_e_record->GetTimestamp();
|
|
bool is_delayed_sampled = p_mode_e_record->IsDelayedSampled();
|
|
uint32_t sample_cnt = p_mode_e_record->GetSampleCount();
|
|
|
|
sprintf(str_buffer,
|
|
"%4d %9x %9" PRIu64 " %2x %5d %7d %7d %7d %7d %8x %8x\n",
|
|
ii,
|
|
perfmon_id,
|
|
timestamp,
|
|
is_delayed_sampled,
|
|
sample_cnt,
|
|
p_mode_e_record->event,
|
|
p_mode_e_record->trig0,
|
|
p_mode_e_record->trig1,
|
|
p_mode_e_record->sampl,
|
|
p_mode_e_record->zero2,
|
|
p_mode_e_record->zero3
|
|
);
|
|
}
|
|
else if (m_record_format == RecordFormatType::ModeE_userdata)
|
|
{
|
|
auto p_mode_e_userdata_record = (ModeERecordUserData*)(p_record);
|
|
sprintf(str_buffer,
|
|
"%4d %9x %5d %7d %4d %2d %7x %7x %8x %9x %9x %9x %9x\n",
|
|
ii,
|
|
p_mode_e_userdata_record->GetPerfmonId(),
|
|
p_mode_e_userdata_record->GetCount(),
|
|
p_mode_e_userdata_record->GetDropped(),
|
|
p_mode_e_userdata_record->GetSD(),
|
|
p_mode_e_userdata_record->GetTM(),
|
|
p_mode_e_userdata_record->data0_3,
|
|
p_mode_e_userdata_record->data4_7,
|
|
p_mode_e_userdata_record->data8_11,
|
|
p_mode_e_userdata_record->data12_15,
|
|
p_mode_e_userdata_record->data16_19,
|
|
p_mode_e_userdata_record->data20_23,
|
|
p_mode_e_userdata_record->data24_27
|
|
);
|
|
}
|
|
record_strings.emplace_back(std::string(str_buffer));
|
|
|
|
if (record_strings.size() > num_records_to_print)
|
|
{
|
|
record_strings.pop_front();
|
|
}
|
|
}
|
|
else if (perfmon_id == PMA_PerfmonId)
|
|
{
|
|
// Do not print PMA records in this function
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
// Reach the end of valid records
|
|
valid_record_end_detected = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (valid_record_end_detected)
|
|
{
|
|
// Early escape when buffer wraparound does not occur
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
// Buffer wraparound
|
|
record_idx_lo = 0;
|
|
record_idx_hi = m_unread_head;
|
|
}
|
|
}
|
|
|
|
for (size_t ii = 0; ii < record_strings.size(); ii++)
|
|
{
|
|
std::cout << record_strings[ii];
|
|
}
|
|
}
|
|
|
|
// Dump the complete buffer starting from beginning to end. Try to parse
|
|
// records if possible, but don't count on it. ONLY FOR DEBUG!
|
|
void SocModeEBuffer::DumpBuffer()
|
|
{
|
|
auto p_records = (ModeERecordRaw*)(m_pma_buffer_cpu_va);
|
|
auto p_record_common_prefix = (PmRecordSocCommonPrefix*)(p_records);
|
|
auto perfmon_id = p_record_common_prefix->GetPerfmonId();
|
|
|
|
// Print the first record
|
|
if (perfmon_id == PMA_PerfmonId)
|
|
{
|
|
PrintRecord(
|
|
p_record_common_prefix, true /* isPmaRecord */, m_record_format);
|
|
}
|
|
else
|
|
{
|
|
PrintRecord(
|
|
p_record_common_prefix, false /* isPmaRecord */, m_record_format);
|
|
}
|
|
|
|
// Iterate through remaining records
|
|
uint32_t matching_records = 0;
|
|
for (uint32_t ii = 1; ii < m_max_records; ii++)
|
|
{
|
|
auto p_record = (ModeERecordRaw*)(m_pma_buffer_cpu_va) + ii;
|
|
|
|
auto p_record_last = (ModeERecordRaw*)(m_pma_buffer_cpu_va) + ii - 1;
|
|
if (memcmp(p_record, p_record_last, sizeof(ModeERecordRaw)) == 0)
|
|
{
|
|
// One more record identical to the previous one: just count it and continue
|
|
++matching_records;
|
|
}
|
|
else
|
|
{
|
|
// Record mismatch
|
|
if (matching_records)
|
|
{
|
|
// If we have counted identical records, dump the count and reset it
|
|
std::cout << matching_records << " more records identical to the previous one...\n";
|
|
matching_records = 0;
|
|
}
|
|
|
|
// Print the new record
|
|
auto p_record_common_prefix = (PmRecordSocCommonPrefix*)(p_record);
|
|
auto perfmon_id = p_record_common_prefix->GetPerfmonId();
|
|
if (perfmon_id == PMA_PerfmonId)
|
|
{
|
|
PrintRecord(
|
|
p_record_common_prefix, true /* isPmaRecord */, m_record_format);
|
|
}
|
|
else
|
|
{
|
|
PrintRecord(
|
|
p_record_common_prefix, false /* isPmaRecord */, m_record_format);
|
|
}
|
|
}
|
|
}
|
|
|
|
// If we have accumulated any identical records at the end, print it
|
|
if (matching_records)
|
|
{
|
|
std::cout << matching_records << " more records identical to the previous one...\n";
|
|
}
|
|
}
|
|
|
|
void SocModeEBuffer::PrintPmaRecords(const size_t num_records_to_print) const
|
|
{
|
|
char str_buffer[256];
|
|
std::deque<std::string> record_strings;
|
|
printf("No. Ptimer DR TotalTrig StartTrig StopTrig Bookmark \n");
|
|
printf("---- --------- -- --------- --------- --------- ---------\n");
|
|
|
|
uint32_t record_idx_lo = m_unread_head;
|
|
uint32_t record_idx_hi = m_max_records;
|
|
bool valid_record_end_detected = false;
|
|
|
|
for (uint32_t circular_buffer_segment = 0;
|
|
circular_buffer_segment < 2;
|
|
circular_buffer_segment++)
|
|
{
|
|
// Iterate records in buffer
|
|
for (uint32_t ii = record_idx_lo; ii < record_idx_hi; ii++)
|
|
{
|
|
auto p_records = (ModeERecordVolta*)(m_pma_buffer_cpu_va);
|
|
auto p_record_common_prefix =
|
|
(PmRecordSocCommonPrefix*)(&p_records[ii]);
|
|
uint32_t perfmon_id = p_record_common_prefix->GetPerfmonId();
|
|
|
|
if (perfmon_id == PMA_PerfmonId) // PMA Record
|
|
{
|
|
const PmaRecordSoc* p_pma_record =
|
|
(const PmaRecordSoc*)(p_record_common_prefix);
|
|
uint64_t timestamp = p_pma_record->GetPtimer();
|
|
uint32_t total_trig_cnt =
|
|
p_pma_record->GetTotalTriggerCount();
|
|
uint32_t start_trig_cnt =
|
|
p_pma_record->GetStartTriggerCount();
|
|
uint32_t stop_trig_cnt =
|
|
p_pma_record->GetStopTriggerCount();
|
|
uint32_t bookmark = p_pma_record->GetBookmark();
|
|
bool is_dropped_record =
|
|
p_pma_record->IsDroppedRecord();
|
|
|
|
sprintf(str_buffer,
|
|
"%4d %9" PRIu64 " %2x %9d %9d %9d %9d\n",
|
|
ii,
|
|
timestamp,
|
|
is_dropped_record,
|
|
total_trig_cnt,
|
|
start_trig_cnt,
|
|
stop_trig_cnt,
|
|
bookmark
|
|
);
|
|
record_strings.emplace_back(std::string(str_buffer));
|
|
|
|
if (record_strings.size() > num_records_to_print)
|
|
{
|
|
record_strings.pop_front();
|
|
}
|
|
}
|
|
else if (perfmon_id) // Mode C/E Record
|
|
{
|
|
}
|
|
else
|
|
{
|
|
// Reach the end of valid records
|
|
valid_record_end_detected = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (valid_record_end_detected)
|
|
{
|
|
// Early escape when buffer wraparound does not occur
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
// Buffer wraparound
|
|
record_idx_lo = 0;
|
|
record_idx_hi = m_unread_head;
|
|
}
|
|
}
|
|
|
|
for (size_t ii = 0; ii < record_strings.size(); ii++)
|
|
{
|
|
std::cout << record_strings[ii];
|
|
}
|
|
}
|