diff --git a/libraries/libstratosphere/source/erpt/srv/erpt_srv_context.cpp b/libraries/libstratosphere/source/erpt/srv/erpt_srv_context.cpp index 99372c2f8..d0564ff7a 100644 --- a/libraries/libstratosphere/source/erpt/srv/erpt_srv_context.cpp +++ b/libraries/libstratosphere/source/erpt/srv/erpt_srv_context.cpp @@ -126,4 +126,13 @@ namespace ams::erpt::srv { return ResultSuccess(); } + Result Context::ClearContext(CategoryId cat) { + /* Make an empty record for the category. */ + auto record = std::make_unique(cat); + R_UNLESS(record != nullptr, erpt::ResultOutOfMemory()); + + /* Submit the context record. */ + return SubmitContextRecord(std::move(record)); + } + } diff --git a/libraries/libstratosphere/source/erpt/srv/erpt_srv_context.hpp b/libraries/libstratosphere/source/erpt/srv/erpt_srv_context.hpp index dba7128b7..0a425d99c 100644 --- a/libraries/libstratosphere/source/erpt/srv/erpt_srv_context.hpp +++ b/libraries/libstratosphere/source/erpt/srv/erpt_srv_context.hpp @@ -40,6 +40,7 @@ namespace ams::erpt::srv { static Result SubmitContext(const ContextEntry *entry, const u8 *data, u32 data_size); static Result SubmitContextRecord(std::unique_ptr record); static Result WriteContextsToReport(Report *report); + static Result ClearContext(CategoryId cat); }; } diff --git a/libraries/libstratosphere/source/erpt/srv/erpt_srv_context_impl.cpp b/libraries/libstratosphere/source/erpt/srv/erpt_srv_context_impl.cpp index 5f6bf06da..41139a53f 100644 --- a/libraries/libstratosphere/source/erpt/srv/erpt_srv_context_impl.cpp +++ b/libraries/libstratosphere/source/erpt/srv/erpt_srv_context_impl.cpp @@ -47,8 +47,7 @@ namespace ams::erpt::srv { R_UNLESS(ctx_size == sizeof(ContextEntry), erpt::ResultInvalidArgument()); R_UNLESS(meta_size == 0 || meta_size == sizeof(ReportMetaData), erpt::ResultInvalidArgument()); - Reporter reporter(report_type, ctx, data, data_size, meta_size != 0 ? meta : nullptr, nullptr, 0, result); - R_TRY(reporter.CreateReport()); + R_TRY(Reporter::CreateReport(report_type, result, ctx, data, data_size, meta_size != 0 ? meta : nullptr, nullptr, 0)); ManagerImpl::NotifyAll(); @@ -70,12 +69,12 @@ namespace ams::erpt::srv { } Result ContextImpl::UpdatePowerOnTime() { - Reporter::UpdatePowerOnTime(); + /* NOTE: Prior to 12.0.0, this set the power on time, but now erpt does it during initialization. */ return ResultSuccess(); } Result ContextImpl::UpdateAwakeTime() { - Reporter::UpdateAwakeTime(); + /* NOTE: Prior to 12.0.0, this set the power on time, but now erpt does it during initialization. */ return ResultSuccess(); } @@ -148,8 +147,7 @@ namespace ams::erpt::srv { R_UNLESS(ctx_size == sizeof(ContextEntry), erpt::ResultInvalidArgument()); R_UNLESS(num_attachments <= AttachmentsPerReportMax, erpt::ResultInvalidArgument()); - Reporter reporter(report_type, ctx, data, data_size, nullptr, attachments, num_attachments, result); - R_TRY(reporter.CreateReport()); + R_TRY(Reporter::CreateReport(report_type, result, ctx, data, data_size, nullptr, attachments, num_attachments)); ManagerImpl::NotifyAll(); @@ -161,18 +159,15 @@ namespace ams::erpt::srv { } Result ContextImpl::RegisterRunningApplet(ncm::ProgramId program_id) { - /* TODO: For greater accuracy, we should support the active applet time list feature added in 12.0.0. */ - return ResultSuccess(); + return Reporter::RegisterRunningApplet(program_id); } Result ContextImpl::UnregisterRunningApplet(ncm::ProgramId program_id) { - /* TODO: For greater accuracy, we should support the active applet time list feature added in 12.0.0. */ - return ResultSuccess(); + return Reporter::UnregisterRunningApplet(program_id); } Result ContextImpl::UpdateAppletSuspendedDuration(ncm::ProgramId program_id, TimeSpanType duration) { - /* TODO: For greater accuracy, we should support the active applet time list feature added in 12.0.0. */ - return ResultSuccess(); + return Reporter::UpdateAppletSuspendedDuration(program_id, duration); } Result ContextImpl::InvalidateForcedShutdownDetection() { diff --git a/libraries/libstratosphere/source/erpt/srv/erpt_srv_context_record.hpp b/libraries/libstratosphere/source/erpt/srv/erpt_srv_context_record.hpp index 6996c2b12..23618c4bb 100644 --- a/libraries/libstratosphere/source/erpt/srv/erpt_srv_context_record.hpp +++ b/libraries/libstratosphere/source/erpt/srv/erpt_srv_context_record.hpp @@ -38,6 +38,10 @@ namespace ams::erpt::srv { explicit ContextRecord(CategoryId category, u32 array_buf_size = ArrayBufferSizeDefault); ~ContextRecord(); + const ContextEntry *GetContextEntryPtr() const { + return std::addressof(this->ctx); + } + Result Initialize(const ContextEntry *ctx_ptr, const u8 *data, u32 data_size); Result Add(FieldId field_id, bool value_bool); diff --git a/libraries/libstratosphere/source/erpt/srv/erpt_srv_main.cpp b/libraries/libstratosphere/source/erpt/srv/erpt_srv_main.cpp index 764e2b1b7..eaa24bbd5 100644 --- a/libraries/libstratosphere/source/erpt/srv/erpt_srv_main.cpp +++ b/libraries/libstratosphere/source/erpt/srv/erpt_srv_main.cpp @@ -89,10 +89,8 @@ namespace ams::erpt::srv { Journal::Restore(); - if (hos::GetVersion() >= hos::Version_12_0_0) { - Reporter::UpdatePowerOnTime(); - Reporter::UpdateAwakeTime(); - } + Reporter::UpdatePowerOnTime(); + Reporter::UpdateAwakeTime(); return ResultSuccess(); } diff --git a/libraries/libstratosphere/source/erpt/srv/erpt_srv_reporter.cpp b/libraries/libstratosphere/source/erpt/srv/erpt_srv_reporter.cpp index 8d4b21690..9704eda89 100644 --- a/libraries/libstratosphere/source/erpt/srv/erpt_srv_reporter.cpp +++ b/libraries/libstratosphere/source/erpt/srv/erpt_srv_reporter.cpp @@ -225,207 +225,304 @@ namespace ams::erpt::srv { } } - } + Result ValidateCreateReportContext(const ContextEntry *ctx) { + R_UNLESS(ctx->category == CategoryId_ErrorInfo, erpt::ResultRequiredContextMissing()); + R_UNLESS(ctx->field_count <= FieldsPerContext, erpt::ResultInvalidArgument()); - Reporter::Reporter(ReportType type, const ContextEntry *ctx, const u8 *data, u32 data_size, const ReportMetaData *meta, const AttachmentId *attachments, u32 num_attachments, Result ctx_r) - : type(type), ctx(ctx), data(data), data_size(data_size), meta(meta), attachments(attachments), num_attachments(num_attachments), occurrence_tick(), ctx_result(ctx_r) - { - /* ... */ - } + const bool found_error_code = util::range::any_of(MakeSpan(ctx->fields, ctx->field_count), [] (const FieldEntry &entry) { + return entry.id == FieldId_ErrorCode; + }); + R_UNLESS(found_error_code, erpt::ResultRequiredFieldMissing()); - Result Reporter::CreateReport() { - R_TRY(this->ValidateReportContext()); - R_TRY(this->CollectUniqueReportFields()); - R_TRY(this->SubmitReportDefaults()); - R_TRY(this->SubmitReportContexts()); - R_TRY(this->LinkAttachments()); - R_TRY(this->CreateReportFile()); - - this->SaveSyslogReportIfRequired(); - return ResultSuccess(); - } - - Result Reporter::ValidateReportContext() { - R_UNLESS(this->ctx->category == CategoryId_ErrorInfo, erpt::ResultRequiredContextMissing()); - R_UNLESS(this->ctx->field_count <= FieldsPerContext, erpt::ResultInvalidArgument()); - - const bool found_error_code = util::range::any_of(MakeSpan(this->ctx->fields, this->ctx->field_count), [] (const FieldEntry &entry) { - return entry.id == FieldId_ErrorCode; - }); - R_UNLESS(found_error_code, erpt::ResultRequiredFieldMissing()); - - return ResultSuccess(); - } - - Result Reporter::CollectUniqueReportFields() { - this->occurrence_tick = os::GetSystemTick(); - if (hos::GetVersion() >= hos::Version_5_0_0) { - this->steady_clock_internal_offset_seconds = time::GetStandardSteadyClockInternalOffset().GetSeconds(); - } else { - this->steady_clock_internal_offset_seconds = 0; + return ResultSuccess(); } - this->report_id.uuid = util::GenerateUuid(); - this->report_id.uuid.ToString(this->identifier_str, sizeof(this->identifier_str)); - if (R_FAILED(time::StandardNetworkSystemClock::GetCurrentTime(std::addressof(this->timestamp_network)))) { - this->timestamp_network = {0}; + + Result SubmitReportDefaults(const ContextEntry *ctx) { + AMS_ASSERT(ctx->category == CategoryId_ErrorInfo); + + auto record = std::make_unique(CategoryId_ErrorInfoDefaults); + R_UNLESS(record != nullptr, erpt::ResultOutOfMemory()); + + bool found_abort_flag = false, found_syslog_flag = false; + for (u32 i = 0; i < ctx->field_count; i++) { + if (ctx->fields[i].id == FieldId_AbortFlag) { + found_abort_flag = true; + } + if (ctx->fields[i].id == FieldId_HasSyslogFlag) { + found_syslog_flag = true; + } + if (found_abort_flag && found_syslog_flag) { + break; + } + } + + if (!found_abort_flag) { + record->Add(FieldId_AbortFlag, false); + } + + if (!found_syslog_flag) { + record->Add(FieldId_HasSyslogFlag, true); + } + + R_TRY(Context::SubmitContextRecord(std::move(record))); + + return ResultSuccess(); } - R_ABORT_UNLESS(time::GetStandardSteadyClockCurrentTimePoint(std::addressof(this->steady_clock_current_timepoint))); - R_TRY(time::StandardUserSystemClock::GetCurrentTime(std::addressof(this->timestamp_user))); - return ResultSuccess(); - } - Result Reporter::SubmitReportDefaults() { - auto record = std::make_unique(CategoryId_ErrorInfoDefaults); - R_UNLESS(record != nullptr, erpt::ResultOutOfMemory()); + void SaveSyslogReportIfRequired(const ContextEntry *ctx, const ReportId &report_id) { + bool needs_save_syslog = true; + for (u32 i = 0; i < ctx->field_count; i++) { + static_assert(FieldToTypeMap[FieldId_HasSyslogFlag] == FieldType_Bool); + if (ctx->fields[i].id == FieldId_HasSyslogFlag && !ctx->fields[i].value_bool) { + needs_save_syslog = false; + break; + } + } - bool found_abort_flag = false, found_syslog_flag = false; - for (u32 i = 0; i < this->ctx->field_count; i++) { - if (this->ctx->fields[i].id == FieldId_AbortFlag) { - found_abort_flag = true; - } - if (this->ctx->fields[i].id == FieldId_HasSyslogFlag) { - found_syslog_flag = true; - } - if (found_abort_flag && found_syslog_flag) { - break; + if (needs_save_syslog) { + /* Here nintendo sends a report to srepo:u (vtable offset 0xE8) with data report_id. */ + /* We will not send report ids to srepo:u. */ } } - if (!found_abort_flag) { - record->Add(FieldId_AbortFlag, false); + void SubmitAppletActiveDurationForCrashReport(const ContextEntry *error_info_ctx, const void *data, u32 data_size, ContextRecord *error_info_auto_record) { + /* Check pre-conditions. */ + AMS_ASSERT(error_info_ctx != nullptr); + AMS_ASSERT(error_info_ctx->category == CategoryId_ErrorInfo); + AMS_ASSERT(data != nullptr); + AMS_ASSERT(error_info_auto_record != nullptr); + + /* Find the program id entry. */ + const auto fields_span = MakeSpan(error_info_ctx->fields, error_info_ctx->field_count); + const auto program_id_entry = util::range::find_if(fields_span, [](const FieldEntry &entry) { return entry.id == FieldId_ProgramId; }); + if (program_id_entry == fields_span.end()) { + return; + } + + /* Check that the report has abort flag set. */ + AMS_ASSERT(util::range::any_of(fields_span, [](const FieldEntry &entry) { return entry.id == FieldId_AbortFlag && entry.value_bool; })); + + /* Check that the program id's value is a string. */ + AMS_ASSERT(program_id_entry->type == FieldType_String); + + /* Check that the program id's length is valid/in bounds. */ + const auto program_id_ofs = program_id_entry->value_array.start_idx; + const auto program_id_len = program_id_entry->value_array.size; + AMS_ASSERT(16 <= program_id_len && program_id_len <= 17); + AMS_ASSERT(program_id_ofs + program_id_len <= data_size); + + /* Get the program id string. */ + char program_id_str[17]; + std::memcpy(program_id_str, static_cast(data) + program_id_ofs, std::min(program_id_len, sizeof(program_id_str))); + program_id_str[sizeof(program_id_str) - 1] = '\x00'; + + /* Convert the string to an integer. */ + char *end_ptr = nullptr; + const ncm::ProgramId program_id = { std::strtoull(program_id_str, std::addressof(end_ptr), 16) }; + AMS_ASSERT(*end_ptr == '\x00'); + + /* Get the active duration. */ + const auto active_duration = g_applet_active_time_info_list.GetActiveDuration(program_id); + if (!active_duration.has_value()) { + return; + } + + /* Add the active applet time. */ + const auto result = error_info_auto_record->Add(FieldId_AppletTotalActiveTime, (*active_duration).GetSeconds()); + R_ASSERT(result); } - if (!found_syslog_flag) { - record->Add(FieldId_HasSyslogFlag, true); + Result LinkAttachments(const ReportId &report_id, const AttachmentId *attachments, u32 num_attachments) { + for (u32 i = 0; i < num_attachments; i++) { + R_TRY(JournalForAttachments::SetOwner(attachments[i], report_id)); + } + return ResultSuccess(); } - R_TRY(Context::SubmitContextRecord(std::move(record))); + Result CreateReportFile(const ReportId &report_id, ReportType type, const ReportMetaData *meta, u32 num_attachments, const time::PosixTime ×tamp_user, const time::PosixTime ×tamp_network, bool redirect_new_reports) { + /* Define journal record deleter. */ + struct JournalRecordDeleter { + void operator()(JournalRecord *record) { + if (record != nullptr) { + if (record->RemoveReference()) { + delete record; + } + } + } + }; + /* Make a journal record. */ + auto record = std::unique_ptr, JournalRecordDeleter>{new JournalRecord, JournalRecordDeleter{}}; + R_UNLESS(record != nullptr, erpt::ResultOutOfMemory()); + record->AddReference(); + + record->info.type = type; + record->info.id = report_id; + record->info.flags = erpt::srv::MakeNoReportFlags(); + record->info.timestamp_user = timestamp_user; + record->info.timestamp_network = timestamp_network; + if (meta != nullptr) { + record->info.meta_data = *meta; + } + if (num_attachments > 0) { + record->info.flags.Set(); + } + + auto report = std::make_unique(record.get(), redirect_new_reports); + R_UNLESS(report != nullptr, erpt::ResultOutOfMemory()); + auto report_guard = SCOPE_GUARD { report->Delete(); }; + + R_TRY(Context::WriteContextsToReport(report.get())); + R_TRY(report->GetSize(std::addressof(record->info.report_size))); + + if (!redirect_new_reports) { + /* If we're not redirecting new reports, then we want to store the report in the journal. */ + R_TRY(Journal::Store(record.get())); + } else { + /* If we are redirecting new reports, we don't want to store the report in the journal. */ + /* We should take this opportunity to delete any attachments associated with the report. */ + R_ABORT_UNLESS(JournalForAttachments::DeleteAttachments(report_id)); + } + + R_TRY(Journal::Commit()); + + report_guard.Cancel(); + return ResultSuccess(); + } + + } + + Result Reporter::RegisterRunningApplet(ncm::ProgramId program_id) { + g_applet_active_time_info_list.Register(program_id); return ResultSuccess(); } - Result Reporter::SubmitReportContexts() { + Result Reporter::UnregisterRunningApplet(ncm::ProgramId program_id) { + g_applet_active_time_info_list.Unregister(program_id); + return ResultSuccess(); + } + + Result Reporter::UpdateAppletSuspendedDuration(ncm::ProgramId program_id, TimeSpan duration) { + g_applet_active_time_info_list.UpdateSuspendedDuration(program_id, duration); + return ResultSuccess(); + } + + Result Reporter::CreateReport(ReportType type, Result ctx_result, const ContextEntry *ctx, const u8 *data, u32 data_size, const ReportMetaData *meta, const AttachmentId *attachments, u32 num_attachments) { + /* Create a context record for the report. */ auto record = std::make_unique(CategoryId_ErrorInfoAuto); R_UNLESS(record != nullptr, erpt::ResultOutOfMemory()); - /* Handle error context. */ - if (R_FAILED(this->ctx_result)) { - SubmitErrorContext(record.get(), this->ctx_result); + /* Initialize the record. */ + R_TRY(record->Initialize(ctx, data, data_size)); + + /* Create the report. */ + return CreateReport(type, ctx_result, std::move(record), meta, attachments, num_attachments); + } + + Result Reporter::CreateReport(ReportType type, Result ctx_result, std::unique_ptr record, const ReportMetaData *meta, const AttachmentId *attachments, u32 num_attachments) { + /* Clear the automatic categories, when we're done with our report. */ + ON_SCOPE_EXIT { + Context::ClearContext(CategoryId_ErrorInfo); + Context::ClearContext(CategoryId_ErrorInfoAuto); + Context::ClearContext(CategoryId_ErrorInfoDefaults); + }; + + /* Get the context entry pointer. */ + const ContextEntry *ctx = record->GetContextEntryPtr(); + + /* Validate the context. */ + R_TRY(ValidateCreateReportContext(ctx)); + + /* Submit report defaults. */ + R_TRY(SubmitReportDefaults(ctx)); + + /* Generate report id. */ + const ReportId report_id = { .uuid = util::GenerateUuid() }; + + /* Get posix timestamps. */ + time::PosixTime timestamp_user; + time::PosixTime timestamp_network; + R_TRY(time::StandardUserSystemClock::GetCurrentTime(std::addressof(timestamp_user))); + if (R_FAILED(time::StandardNetworkSystemClock::GetCurrentTime(std::addressof(timestamp_network)))) { + timestamp_network = {}; } - record->Add(FieldId_OsVersion, s_os_version, util::Strnlen(s_os_version, sizeof(s_os_version))); - record->Add(FieldId_PrivateOsVersion, s_private_os_version, util::Strnlen(s_private_os_version, sizeof(s_private_os_version))); - record->Add(FieldId_SerialNumber, s_serial_number, util::Strnlen(s_serial_number, sizeof(s_serial_number))); - record->Add(FieldId_ReportIdentifier, this->identifier_str, sizeof(this->identifier_str)); - record->Add(FieldId_OccurrenceTimestamp, this->timestamp_user.value); - record->Add(FieldId_OccurrenceTimestampNet, this->timestamp_network.value); - record->Add(FieldId_ReportVisibilityFlag, this->type == ReportType_Visible); - record->Add(FieldId_OccurrenceTick, this->occurrence_tick.GetInt64Value()); - record->Add(FieldId_SteadyClockInternalOffset, this->steady_clock_internal_offset_seconds); - record->Add(FieldId_SteadyClockCurrentTimePointValue, this->steady_clock_current_timepoint.value); + /* Save syslog report, if required. */ + SaveSyslogReportIfRequired(ctx, report_id); + + /* Submit report contexts. */ + R_TRY(SubmitReportContexts(report_id, type, ctx_result, std::move(record), timestamp_user, timestamp_network)); + + /* Link attachments to the report. */ + R_TRY(LinkAttachments(report_id, attachments, num_attachments)); + + /* Create the report file. */ + R_TRY(CreateReportFile(report_id, type, meta, num_attachments, timestamp_user, timestamp_network, s_redirect_new_reports)); + + return ResultSuccess(); + } + + Result Reporter::SubmitReportContexts(const ReportId &report_id, ReportType type, Result ctx_result, std::unique_ptr record, const time::PosixTime ×tamp_user, const time::PosixTime ×tamp_network) { + /* Create automatic record. */ + auto auto_record = std::make_unique(CategoryId_ErrorInfoAuto, 0x300); + R_UNLESS(auto_record != nullptr, erpt::ResultOutOfMemory()); + + /* Handle error context. */ + if (R_FAILED(ctx_result)) { + SubmitErrorContext(auto_record.get(), ctx_result); + } + + /* Collect unique report fields. */ + char identifier_str[0x40]; + report_id.uuid.ToString(identifier_str, sizeof(identifier_str)); + + const auto occurrence_tick = os::GetSystemTick(); + const s64 steady_clock_internal_offset_seconds = (hos::GetVersion() >= hos::Version_5_0_0) ? time::GetStandardSteadyClockInternalOffset().GetSeconds() : 0; + + time::SteadyClockTimePoint steady_clock_current_timepoint; + R_ABORT_UNLESS(time::GetStandardSteadyClockCurrentTimePoint(std::addressof(steady_clock_current_timepoint))); + + /* Add automatic fields. */ + auto_record->Add(FieldId_OsVersion, s_os_version, util::Strnlen(s_os_version, sizeof(s_os_version))); + auto_record->Add(FieldId_PrivateOsVersion, s_private_os_version, util::Strnlen(s_private_os_version, sizeof(s_private_os_version))); + auto_record->Add(FieldId_SerialNumber, s_serial_number, util::Strnlen(s_serial_number, sizeof(s_serial_number))); + auto_record->Add(FieldId_ReportIdentifier, identifier_str, util::Strnlen(identifier_str, sizeof(identifier_str))); + auto_record->Add(FieldId_OccurrenceTimestamp, timestamp_user.value); + auto_record->Add(FieldId_OccurrenceTimestampNet, timestamp_network.value); + auto_record->Add(FieldId_ReportVisibilityFlag, type == ReportType_Visible); + auto_record->Add(FieldId_OccurrenceTick, occurrence_tick.GetInt64Value()); + auto_record->Add(FieldId_SteadyClockInternalOffset, steady_clock_internal_offset_seconds); + auto_record->Add(FieldId_SteadyClockCurrentTimePointValue, steady_clock_current_timepoint.value); + auto_record->Add(FieldId_ElapsedTimeSincePowerOn, (occurrence_tick - *s_power_on_time).ToTimeSpan().GetSeconds()); + auto_record->Add(FieldId_ElapsedTimeSinceLastAwake, (occurrence_tick - *s_awake_time).ToTimeSpan().GetSeconds()); if (s_initial_launch_settings_completion_time) { s64 elapsed_seconds; - if (R_SUCCEEDED(time::GetElapsedSecondsBetween(std::addressof(elapsed_seconds), *s_initial_launch_settings_completion_time, this->steady_clock_current_timepoint))) { - record->Add(FieldId_ElapsedTimeSinceInitialLaunch, elapsed_seconds); + if (R_SUCCEEDED(time::GetElapsedSecondsBetween(std::addressof(elapsed_seconds), *s_initial_launch_settings_completion_time, steady_clock_current_timepoint))) { + auto_record->Add(FieldId_ElapsedTimeSinceInitialLaunch, elapsed_seconds); } } - if (s_power_on_time) { - record->Add(FieldId_ElapsedTimeSincePowerOn, (this->occurrence_tick - *s_power_on_time).ToTimeSpan().GetSeconds()); - } - - if (s_awake_time) { - record->Add(FieldId_ElapsedTimeSinceLastAwake, (this->occurrence_tick - *s_awake_time).ToTimeSpan().GetSeconds()); - } - if (s_application_launch_time) { - record->Add(FieldId_ApplicationAliveTime, (this->occurrence_tick - *s_application_launch_time).ToTimeSpan().GetSeconds()); + auto_record->Add(FieldId_ApplicationAliveTime, (occurrence_tick - *s_application_launch_time).ToTimeSpan().GetSeconds()); } - /* TODO: Add FieldId_AppletTotalActiveTime. */ + /* Submit applet active duration information. */ + { + const auto *error_info_ctx = record->GetContextEntryPtr(); + SubmitAppletActiveDurationForCrashReport(error_info_ctx, error_info_ctx->array_buffer, error_info_ctx->array_buffer_size - error_info_ctx->array_free_count, auto_record.get()); + } + /* Submit the auto record. */ + R_TRY(Context::SubmitContextRecord(std::move(auto_record))); + + /* Submit the info record. */ R_TRY(Context::SubmitContextRecord(std::move(record))); - R_TRY(Context::SubmitContext(this->ctx, this->data, this->data_size)); - /* Submit context for resource limits. */ SubmitResourceLimitContexts(); return ResultSuccess(); } - Result Reporter::LinkAttachments() { - for (u32 i = 0; i < this->num_attachments; i++) { - R_TRY(JournalForAttachments::SetOwner(this->attachments[i], this->report_id)); - } - return ResultSuccess(); - } - - Result Reporter::CreateReportFile() { - /* Define journal record deleter. */ - struct JournalRecordDeleter { - void operator()(JournalRecord *record) { - if (record != nullptr) { - if (record->RemoveReference()) { - delete record; - } - } - } - }; - - /* Make a journal record. */ - auto record = std::unique_ptr, JournalRecordDeleter>{new JournalRecord, JournalRecordDeleter{}}; - R_UNLESS(record != nullptr, erpt::ResultOutOfMemory()); - record->AddReference(); - - record->info.type = this->type; - record->info.id = this->report_id; - record->info.flags = erpt::srv::MakeNoReportFlags(); - record->info.timestamp_user = this->timestamp_user; - record->info.timestamp_network = this->timestamp_network; - if (this->meta != nullptr) { - record->info.meta_data = *this->meta; - } - if (this->num_attachments > 0) { - record->info.flags.Set(); - } - - auto report = std::make_unique(record.get(), s_redirect_new_reports); - R_UNLESS(report != nullptr, erpt::ResultOutOfMemory()); - auto report_guard = SCOPE_GUARD { report->Delete(); }; - - R_TRY(Context::WriteContextsToReport(report.get())); - R_TRY(report->GetSize(std::addressof(record->info.report_size))); - - if (!s_redirect_new_reports) { - /* If we're not redirecting new reports, then we want to store the report in the journal. */ - R_TRY(Journal::Store(record.get())); - } else { - /* If we are redirecting new reports, we don't want to store the report in the journal. */ - /* We should take this opportunity to delete any attachments associated with the report. */ - R_ABORT_UNLESS(JournalForAttachments::DeleteAttachments(this->report_id)); - } - - R_TRY(Journal::Commit()); - - report_guard.Cancel(); - return ResultSuccess(); - } - - void Reporter::SaveSyslogReportIfRequired() { - bool needs_save_syslog = true; - for (u32 i = 0; i < this->ctx->field_count; i++) { - static_assert(FieldToTypeMap[FieldId_HasSyslogFlag] == FieldType_Bool); - if (this->ctx->fields[i].id == FieldId_HasSyslogFlag && (this->ctx->fields[i].value_bool == false)) { - needs_save_syslog = false; - break; - } - } - if (needs_save_syslog) { - /* Here nintendo sends a report to srepo:u (vtable offset 0xE8) with data this->report_id. */ - /* We will not send report ids to srepo:u. */ - } - } - } diff --git a/libraries/libstratosphere/source/erpt/srv/erpt_srv_reporter.hpp b/libraries/libstratosphere/source/erpt/srv/erpt_srv_reporter.hpp index 9a4adca89..18460fc0b 100644 --- a/libraries/libstratosphere/source/erpt/srv/erpt_srv_reporter.hpp +++ b/libraries/libstratosphere/source/erpt/srv/erpt_srv_reporter.hpp @@ -15,6 +15,7 @@ */ #pragma once #include +#include "erpt_srv_context_record.hpp" namespace ams::erpt::srv { @@ -28,22 +29,6 @@ namespace ams::erpt::srv { static std::optional s_awake_time; static std::optional s_power_on_time; static std::optional s_initial_launch_settings_completion_time; - private: - const ReportType type; - const ContextEntry * const ctx; - const u8 * const data; - const u32 data_size; - const ReportMetaData * const meta; - const AttachmentId * const attachments; - const u32 num_attachments; - char identifier_str[0x40]; - time::PosixTime timestamp_user; - time::PosixTime timestamp_network; - os::Tick occurrence_tick; - s64 steady_clock_internal_offset_seconds; - ReportId report_id; - Result ctx_result; - time::SteadyClockTimePoint steady_clock_current_timepoint; public: static void ClearApplicationLaunchTime() { s_application_launch_time = std::nullopt; } static void ClearInitialLaunchSettingsCompletionTime() { s_initial_launch_settings_completion_time = std::nullopt; } @@ -65,20 +50,16 @@ namespace ams::erpt::srv { return ResultSuccess(); } + static Result RegisterRunningApplet(ncm::ProgramId program_id); + static Result UnregisterRunningApplet(ncm::ProgramId program_id); + static Result UpdateAppletSuspendedDuration(ncm::ProgramId program_id, TimeSpan duration); + static void SetRedirectNewReportsToSdCard(bool en) { s_redirect_new_reports = en; } private: - Result ValidateReportContext(); - Result CollectUniqueReportFields(); - Result SubmitReportDefaults(); - Result SubmitReportContexts(); - Result LinkAttachments(); - Result CreateReportFile(); - void SaveSyslogReportIfRequired(); - void SaveSyslogReport(); + static Result SubmitReportContexts(const ReportId &report_id, ReportType type, Result ctx_result, std::unique_ptr record, const time::PosixTime &user_timestamp, const time::PosixTime &network_timestamp); public: - Reporter(ReportType type, const ContextEntry *ctx, const u8 *data, u32 data_size, const ReportMetaData *meta, const AttachmentId *attachments, u32 num_attachments, Result ctx_result); - - Result CreateReport(); + static Result CreateReport(ReportType type, Result ctx_result, const ContextEntry *ctx, const u8 *data, u32 data_size, const ReportMetaData *meta, const AttachmentId *attachments, u32 num_attachments); + static Result CreateReport(ReportType type, Result ctx_result, std::unique_ptr record, const ReportMetaData *meta, const AttachmentId *attachments, u32 num_attachments); }; }