Implement Exceptional Signal Handler + Fix Destruction Behavior

An exceptional signal handler allows us to convert an OS signal into a C++ exception, this allows us to alleviate a lot of crashes that would otherwise occur from signals being thrown during execution of games and be able to handle them gracefully.
This commit is contained in:
◱ PixelyIon 2020-11-03 15:14:09 +05:30 committed by ◱ PixelyIon
parent 3cde568c51
commit 668f623256
45 changed files with 692 additions and 312 deletions

View File

@ -82,8 +82,8 @@
<rule entity="ENUMERATOR" visibility="ANY" specifier="ANY" prefix="" style="PASCAL_CASE" suffix="" />
<rule entity="TYPEDEF" visibility="ANY" specifier="ANY" prefix="" style="PASCAL_CASE" suffix="" />
<rule entity="UNION" visibility="ANY" specifier="ANY" prefix="" style="PASCAL_CASE" suffix="" />
<rule entity="MEMBER_FUNCTION" visibility="ANY" specifier="ANY" prefix="" style="PASCAL_CASE" suffix="" />
<rule entity="MEMBER_FIELD" visibility="ANY" specifier="ANY" prefix="" style="CAMEL_CASE" suffix="" />
<rule entity="CLASS_MEMBER_FUNCTION,STRUCT_MEMBER_FUNCTION" visibility="ANY" specifier="ANY" prefix="" style="PASCAL_CASE" suffix="" />
<rule entity="CLASS_MEMBER_FIELD,STRUCT_MEMBER_FIELD" visibility="ANY" specifier="ANY" prefix="" style="CAMEL_CASE" suffix="" />
<rule entity="GLOBAL_FUNCTION" visibility="ANY" specifier="ANY" prefix="" style="PASCAL_CASE" suffix="" />
<rule entity="GLOBAL_VARIABLE" visibility="ANY" specifier="ANY" prefix="" style="PASCAL_CASE" suffix="" />
<rule entity="PARAMETER" visibility="ANY" specifier="ANY" prefix="" style="CAMEL_CASE" suffix="" />

6
.idea/compiler.xml Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CompilerConfiguration">
<bytecodeTargetLevel target="1.8" />
</component>
</project>

View File

@ -2,6 +2,6 @@
<project version="4">
<component name="DiscordIntegrationProjectSettings" description="Lightswitch is an experimental Nintendo Switch emulator for Android phones." />
<component name="DiscordProjectSettings">
<option name="show" value="true" />
<option name="show" value="PROJECT_FILES" />
</component>
</project>

View File

@ -175,6 +175,7 @@
<inspection_tool class="AndroidLintJobSchedulerService" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AndroidLintKeyboardInaccessibleWidget" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AndroidLintKotlinPropertyAccess" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="AndroidLintKtxExtensionAvailable" enabled="false" level="INFO" enabled_by_default="false" />
<inspection_tool class="AndroidLintLabelFor" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AndroidLintLambdaLast" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="AndroidLintLibraryCustomView" enabled="true" level="ERROR" enabled_by_default="true" />
@ -226,6 +227,7 @@
<inspection_tool class="AndroidLintNewerVersionAvailable" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="AndroidLintNfcTechWhitespace" enabled="true" level="ERROR" enabled_by_default="true" />
<inspection_tool class="AndroidLintNoHardKeywords" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="AndroidLintNonConstantResourceId" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="AndroidLintNonResizeableActivity" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="AndroidLintNotInterpolated" enabled="true" level="ERROR" enabled_by_default="true" />
<inspection_tool class="AndroidLintNotSibling" enabled="true" level="ERROR" enabled_by_default="true" />
@ -257,6 +259,8 @@
<inspection_tool class="AndroidLintProtectedPermissions" enabled="true" level="ERROR" enabled_by_default="true" />
<inspection_tool class="AndroidLintProxyPassword" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AndroidLintPxUsage" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AndroidLintQueryAllPackagesPermission" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="AndroidLintQueryPermissionsNeeded" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="AndroidLintRange" enabled="true" level="ERROR" enabled_by_default="true" />
<inspection_tool class="AndroidLintRecycle" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AndroidLintRecyclerView" enabled="true" level="WARNING" enabled_by_default="true" />
@ -280,6 +284,7 @@
<inspection_tool class="AndroidLintSQLiteString" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AndroidLintSSLCertificateSocketFactoryCreateSocket" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AndroidLintSSLCertificateSocketFactoryGetInsecure" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AndroidLintScopedStorage" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="AndroidLintScrollViewCount" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AndroidLintScrollViewSize" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AndroidLintSdCardPath" enabled="true" level="WARNING" enabled_by_default="true" />
@ -407,11 +412,13 @@
<option name="m_limit" value="1" />
</inspection_tool>
<inspection_tool class="AnonymousClassVariableHidesContainingMethodVariable" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AnonymousHasLambdaAlternative" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="AnonymousInnerClass" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AnonymousInnerClassMayBeStatic" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ApiName" enabled="true" level="ERROR" enabled_by_default="true" />
<inspection_tool class="ApiNamespace" enabled="true" level="ERROR" enabled_by_default="true" />
<inspection_tool class="ApiParameter" enabled="true" level="ERROR" enabled_by_default="true" />
<inspection_tool class="ArgumentSelectionDefects" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="ArrayCanBeReplacedWithEnumValues" enabled="true" level="INFORMATION" enabled_by_default="true" />
<inspection_tool class="ArrayCreationWithoutNewKeyword" enabled="true" level="INFORMATION" enabled_by_default="true" />
<inspection_tool class="ArrayEquality" enabled="true" level="WARNING" enabled_by_default="true" />
@ -561,6 +568,7 @@
<inspection_tool class="CheckValidXmlInScriptTagBody" enabled="true" level="ERROR" enabled_by_default="true" />
<inspection_tool class="CheckXmlFileWithXercesValidator" enabled="true" level="ERROR" enabled_by_default="true" />
<inspection_tool class="CheckedExceptionClass" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ClangTidy" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="ClangTidyInspection" enabled="false" level="WARNING" enabled_by_default="false">
<option name="useCustomListOfClangTidyChecks" value="false" />
</inspection_tool>
@ -672,7 +680,7 @@
<inspection_tool class="ConstantAssertCondition" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ConstantConditionIf" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
<inspection_tool class="ConstantConditionalExpression" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ConstantConditions" enabled="true" level="WARNING" enabled_by_default="true">
<inspection_tool class="ConstantConditions" enabled="false" level="WARNING" enabled_by_default="false">
<option name="SUGGEST_NULLABLE_ANNOTATIONS" value="false" />
<option name="DONT_REPORT_TRUE_ASSERT_STATEMENTS" value="false" />
</inspection_tool>
@ -825,6 +833,7 @@
<option name="ignoreThrowables" value="true" />
<option name="commentsAreContent" value="true" />
</inspection_tool>
<inspection_tool class="EmptyDeclOrStmt" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="EmptyDirectory" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="EmptyFinallyBlock" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="EmptyInitializer" enabled="true" level="WARNING" enabled_by_default="true" />
@ -931,10 +940,12 @@
</inspection_tool>
<inspection_tool class="ForeachStatement" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="FormatSpecifiers" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ForwardCompatibility" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="FrequentlyUsedInheritorInspection" enabled="true" level="INFORMATION" enabled_by_default="true" />
<inspection_tool class="FromClosedRangeMigration" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
<inspection_tool class="FullJavaName" enabled="true" level="ERROR" enabled_by_default="true" />
<inspection_tool class="FullMethodName" enabled="true" level="ERROR" enabled_by_default="true" />
<inspection_tool class="FunctionImplicitDeclaration" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="FunctionName" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
<inspection_tool class="FunctionParameterCountMismatch" enabled="true" level="ERROR" enabled_by_default="true" />
<inspection_tool class="FunctionWithLambdaExpressionBody" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
@ -1283,7 +1294,9 @@
<inspection_tool class="JUnitDatapoint" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="JUnitRule" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="JUnitTestNG" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="Java8ListSort" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="Java9CollectionFactory" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="Java9ModuleExportsPackageToItself" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="Java9RedundantRequiresStatement" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="Java9ReflectionClassVisibility" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="Java9UndeclaredServiceUsage" enabled="true" level="WARNING" enabled_by_default="true" />
@ -1324,6 +1337,7 @@
<inspection_tool class="JavaLangImport" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="JavaLangInvokeHandleSignature" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="JavaMapForEach" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
<inspection_tool class="JavaModuleNaming" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="JavaReflectionInvocation" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="JavaReflectionMemberAccess" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="JavaRequiresAutoModule" enabled="true" level="WARNING" enabled_by_default="true" />
@ -1360,9 +1374,11 @@
<inspection_tool class="KotlinDeprecation" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="KotlinDoubleNegation" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
<inspection_tool class="KotlinEqualsBetweenInconvertibleTypes" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="KotlinInternalExternalFunction" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="KotlinInternalInJava" enabled="true" level="ERROR" enabled_by_default="true" />
<inspection_tool class="KotlinInvalidBundleOrProperty" enabled="true" level="ERROR" enabled_by_default="true" />
<inspection_tool class="KotlinJniMissingFunction" enabled="true" level="ERROR" enabled_by_default="true" />
<inspection_tool class="KotlinLoggerInitializedWithForeignClass" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="KotlinRedundantOverride" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
<inspection_tool class="KotlinThrowableNotThrown" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="KotlinUnusedImport" enabled="true" level="WARNING" enabled_by_default="true" />
@ -1639,6 +1655,7 @@
<inspection_tool class="NotifyWithoutCorrespondingWait" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="NullArgumentToVariableArgMethod" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="NullChecksToSafeCall" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
<inspection_tool class="NullDereferences" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="NullThrown" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="NullableBooleanElvis" enabled="true" level="INFO" enabled_by_default="true" />
<inspection_tool class="NullableProblems" enabled="true" level="WARNING" enabled_by_default="true">
@ -1654,11 +1671,14 @@
<inspection_tool class="NumberEquality" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="NumericOverflow" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="NumericToString" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="OCDFA" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="OCGlobalUnused" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="OCInconsistentNaming" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
<inspection_tool class="OCLegacyObjCLiteral" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="OCLoopDoesntUseConditionVariable" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="OCNotLocalizedString" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="OCNotReleasedIvar" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="OCSimplify" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="OCUnusedClass" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="OCUnusedClassInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
<inspection_tool class="OCUnusedConcept" enabled="false" level="WARNING" enabled_by_default="false" />
@ -1694,6 +1714,7 @@
<option name="ignoreRequiredObsoleteCollectionTypes" value="true" />
</inspection_tool>
<inspection_tool class="ObsoleteExperimentalCoroutines" enabled="true" level="ERROR" enabled_by_default="true" />
<inspection_tool class="ObsoleteKotlinJsPackages" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="ObviousNullCheck" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="OctalAndDecimalIntegersMixed" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="OctalLiteral" enabled="true" level="WARNING" enabled_by_default="true" />
@ -1782,6 +1803,7 @@
<inspection_tool class="PatternValidation" enabled="true" level="WARNING" enabled_by_default="true">
<option name="CHECK_NON_CONSTANT_VALUES" value="true" />
</inspection_tool>
<inspection_tool class="PatternVariableCanBeUsed" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="PermissionUsageInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="PlaceholderCountMatchesArgumentCount" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="PlatformExtensionReceiverOfInline" enabled="true" level="WARNING" enabled_by_default="true" />
@ -1799,6 +1821,7 @@
<inspection_tool class="PointlessNullCheck" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="PreviewAnnotationInFunctionWithParameters" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="PreviewDimensionRespectsLimit" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="PreviewMultipleParameterProviders" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="PreviewMustBeTopLevelFunction" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="PreviewNeedsComposableAnnotation" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="PrimitiveArrayArgumentToVariableArgMethod" enabled="true" level="WARNING" enabled_by_default="true" />
@ -1807,6 +1830,10 @@
<inspection_tool class="PrivatePropertyName" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
<inspection_tool class="ProblematicVarargsMethodOverride" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ProblematicWhitespace" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="ProhibitJvmOverloadsOnConstructorsOfAnnotationClassesMigration" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="ProhibitRepeatedUseSiteTargetAnnotationsMigration" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="ProhibitTypeParametersForLocalVariablesMigration" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="ProhibitUseSiteTargetAnnotationsOnSuperTypesMigration" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="ProjectFingerprint" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="PropertyAndIvarTypeMismatch" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="PropertyName" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
@ -1958,6 +1985,7 @@
<inspection_tool class="RecursiveEqualsCall" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="RecursivePropertyAccessor" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="RedundantArrayCreation" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="RedundantAsSequence" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
<inspection_tool class="RedundantAsync" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
<inspection_tool class="RedundantCast" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="RedundantClassCall" enabled="true" level="WARNING" enabled_by_default="true" />
@ -1966,6 +1994,7 @@
<inspection_tool class="RedundantComparatorComparing" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="RedundantCompareCall" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="RedundantElseInIf" enabled="true" level="INFORMATION" enabled_by_default="true" />
<inspection_tool class="RedundantElvisReturnNull" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="RedundantEmptyInitializerBlock" enabled="true" level="INFO" enabled_by_default="true" />
<inspection_tool class="RedundantEnumConstructorInvocation" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
<inspection_tool class="RedundantExplicitClose" enabled="true" level="WARNING" enabled_by_default="true" />
@ -1978,6 +2007,8 @@
<option name="ignoreSerializable" value="false" />
<option name="ignoreCloneable" value="false" />
</inspection_tool>
<inspection_tool class="RedundantInnerClassModifier" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="RedundantLabelMigration" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
<inspection_tool class="RedundantLabeledSwitchRuleCodeBlock" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="RedundantLambdaArrow" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
<inspection_tool class="RedundantLambdaParameterType" enabled="true" level="INFORMATION" enabled_by_default="true" />
@ -1986,6 +2017,7 @@
<inspection_tool class="RedundantNotNullExtensionReceiverOfInline" enabled="false" level="INFORMATION" enabled_by_default="false" />
<inspection_tool class="RedundantObjectTypeCheck" enabled="true" level="INFORMATION" enabled_by_default="true" />
<inspection_tool class="RedundantOperationOnEmptyContainer" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="RedundantRecordConstructor" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="RedundantRequireNotNullCall" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
<inspection_tool class="RedundantReturnLabel" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
<inspection_tool class="RedundantRunCatching" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
@ -2071,8 +2103,10 @@
<inspection_tool class="RequiredAttributes" enabled="true" level="WARNING" enabled_by_default="true">
<option name="myAdditionalRequiredHtmlAttributes" value="" />
</inspection_tool>
<inspection_tool class="ResourceNotFound" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="ResourceParameter" enabled="true" level="ERROR" enabled_by_default="true" />
<inspection_tool class="RestSignature" enabled="true" level="ERROR" enabled_by_default="true" />
<inspection_tool class="RestrictReturnStatementTargetMigration" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="ResultOfObjectAllocationIgnored" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ResultSetIndexZero" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ReturnFromFinallyBlock" enabled="true" level="WARNING" enabled_by_default="true" />
@ -2130,6 +2164,7 @@
<inspection_tool class="ShellCheck" enabled="true" level="ERROR" enabled_by_default="true" />
<inspection_tool class="ShiftOutOfRange" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ShrinkerArrayType" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="ShrinkerInnerClassSeparator" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="ShrinkerInvalidFlags" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="ShrinkerUnresolvedReference" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="SignalWithoutCorrespondingAwait" enabled="true" level="WARNING" enabled_by_default="true" />
@ -2234,6 +2269,7 @@
<inspection_tool class="SuspiciousArrayMethodCall" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="SuspiciousAsDynamic" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
<inspection_tool class="SuspiciousCollectionReassignment" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="SuspiciousDateFormat" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SuspiciousEqualsCombination" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
<inspection_tool class="SuspiciousGetterSetter" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="SuspiciousIndentAfterControlStatement" enabled="true" level="WARNING" enabled_by_default="true" />
@ -2342,6 +2378,7 @@
<option name="m_onlyLookAtBlocks" value="false" />
</inspection_tool>
<inspection_tool class="TooBroadThrows" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="TrailingComma" enabled="false" level="INFO" enabled_by_default="false" />
<inspection_tool class="TrailingSpacesInProperty" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="TransientFieldInNonSerializableClass" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="TransientFieldNotInitialized" enabled="true" level="WARNING" enabled_by_default="true" />
@ -2449,6 +2486,7 @@
<option name="m_ignoreStaticMethodCalls" value="false" />
<option name="m_ignoreStaticAccessFromStaticContext" value="false" />
</inspection_tool>
<inspection_tool class="UnreachableCode" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="UnresolvedCollectionMessage" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="UnresolvedMessage" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="UnresolvedPropertyKey" enabled="true" level="ERROR" enabled_by_default="true" />
@ -2462,23 +2500,31 @@
<option name="REPORT_POSTFIX_EXPRESSIONS" value="true" />
<option name="REPORT_REDUNDANT_INITIALIZER" value="true" />
</inspection_tool>
<inspection_tool class="UnusedClass" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="UnusedConcept" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="UnusedDefine" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="UnusedEquals" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="UnusedExpressionResult" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
<inspection_tool class="UnusedGlobalDeclaration" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="UnusedImportStatement" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="UnusedIncludeDirective" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
<inspection_tool class="UnusedInstanceVariable" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="UnusedLabel" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="UnusedLambdaExpressionBody" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="UnusedLibrary" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="UnusedLocalVariable" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
<inspection_tool class="UnusedLocalization" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
<inspection_tool class="UnusedMacro" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="UnusedMainParameter" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
<inspection_tool class="UnusedMessageFormatParameter" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="UnusedMethod" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="UnusedParameter" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
<inspection_tool class="UnusedProperty" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="UnusedProperty" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="UnusedReceiverParameter" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="UnusedReturnValue" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="UnusedStruct" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="UnusedSymbol" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="UnusedTemplateParameter" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="UnusedUnaryOperator" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="UnusedValue" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
<inspection_tool class="UpperCaseFieldNameNotConstant" enabled="true" level="WARNING" enabled_by_default="true" />
@ -2527,6 +2573,7 @@
<inspection_tool class="WaitOrAwaitWithoutTimeout" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="WaitWhileHoldingTwoLocks" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="WaitWithoutCorrespondingNotify" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="WarningOnMainUnusedParameterMigration" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
<inspection_tool class="WeakerAccess" enabled="true" level="WARNING" enabled_by_default="true">
<option name="SUGGEST_PACKAGE_LOCAL_FOR_MEMBERS" value="true" />
<option name="SUGGEST_PACKAGE_LOCAL_FOR_TOP_CLASSES" value="true" />

6
.idea/kotlinc.xml Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Kotlin2JvmCompilerArguments">
<option name="jvmTarget" value="14" />
</component>
</project>

View File

@ -43,7 +43,7 @@
</value>
</option>
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" project-jdk-name="JDK" project-jdk-type="JavaSDK">
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" default="true" project-jdk-name="JDK" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build/classes" />
</component>
<component name="ProjectType">

View File

@ -1,11 +1,12 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Controller Configuration" type="AndroidRunConfigurationType" factoryName="Android App" activateToolWindowBeforeRun="false">
<module name="app" />
<module name="Skyline.app" />
<option name="DEPLOY" value="true" />
<option name="DEPLOY_APK_FROM_BUNDLE" value="false" />
<option name="DEPLOY_AS_INSTANT" value="false" />
<option name="ARTIFACT_NAME" value="" />
<option name="PM_INSTALL_OPTIONS" value="" />
<option name="ALL_USERS" value="false" />
<option name="DYNAMIC_FEATURES_DISABLED_LIST" value="" />
<option name="ACTIVITY_EXTRA_FLAGS" value="" />
<option name="MODE" value="specific_activity" />
@ -41,11 +42,16 @@
</Native>
<Profilers>
<option name="ADVANCED_PROFILING_ENABLED" value="false" />
<option name="STARTUP_PROFILING_ENABLED" value="false" />
<option name="STARTUP_CPU_PROFILING_ENABLED" value="false" />
<option name="STARTUP_CPU_PROFILING_CONFIGURATION_NAME" value="Sample Java Methods" />
<option name="STARTUP_NATIVE_MEMORY_PROFILING_ENABLED" value="false" />
<option name="NATIVE_MEMORY_SAMPLE_RATE_BYTES" value="2048" />
</Profilers>
<option name="DEEP_LINK" value="" />
<option name="ACTIVITY_CLASS" value="emu.skyline.input.ControllerActivity" />
<option name="SEARCH_ACTIVITY_IN_GLOBAL_SCOPE" value="false" />
<option name="SKIP_ACTIVITY_VALIDATION" value="false" />
<method v="2">
<option name="Android.Gradle.BeforeRunTask" enabled="true" />
</method>

View File

@ -1,11 +1,12 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Setting" type="AndroidRunConfigurationType" factoryName="Android App" activateToolWindowBeforeRun="false">
<module name="app" />
<module name="Skyline.app" />
<option name="DEPLOY" value="true" />
<option name="DEPLOY_APK_FROM_BUNDLE" value="false" />
<option name="DEPLOY_AS_INSTANT" value="false" />
<option name="ARTIFACT_NAME" value="" />
<option name="PM_INSTALL_OPTIONS" value="" />
<option name="ALL_USERS" value="false" />
<option name="DYNAMIC_FEATURES_DISABLED_LIST" value="" />
<option name="ACTIVITY_EXTRA_FLAGS" value="" />
<option name="MODE" value="specific_activity" />
@ -41,11 +42,16 @@
</Native>
<Profilers>
<option name="ADVANCED_PROFILING_ENABLED" value="false" />
<option name="STARTUP_PROFILING_ENABLED" value="false" />
<option name="STARTUP_CPU_PROFILING_ENABLED" value="false" />
<option name="STARTUP_CPU_PROFILING_CONFIGURATION_NAME" value="Sample Java Methods" />
<option name="STARTUP_NATIVE_MEMORY_PROFILING_ENABLED" value="false" />
<option name="NATIVE_MEMORY_SAMPLE_RATE_BYTES" value="2048" />
</Profilers>
<option name="DEEP_LINK" value="" />
<option name="ACTIVITY_CLASS" value="emu.skyline.SettingsActivity" />
<option name="SEARCH_ACTIVITY_IN_GLOBAL_SCOPE" value="false" />
<option name="SKIP_ACTIVITY_VALIDATION" value="false" />
<method v="2">
<option name="Android.Gradle.BeforeRunTask" enabled="true" />
</method>

View File

@ -1,3 +1,3 @@
<component name="DependencyValidationManager">
<scope name="SkylineNative" pattern="file[app]:src/main/cpp//*.cpp||file[app]:src/main/cpp//*.h||file[app]:src/main/cpp//*.S" />
<scope name="SkylineNative" pattern="file[Skyline.app]:src/main/cpp//*.cpp||file[Skyline.app]:src/main/cpp//*.h||file[Skyline.app]:src/main/cpp//*.S" />
</component>

View File

@ -34,6 +34,8 @@ add_library(skyline SHARED
${source_DIR}/emu_jni.cpp
${source_DIR}/loader_jni.cpp
${source_DIR}/skyline/common.cpp
${source_DIR}/skyline/common/settings.cpp
${source_DIR}/skyline/common/signal.cpp
${source_DIR}/skyline/nce/guest.S
${source_DIR}/skyline/nce.cpp
${source_DIR}/skyline/jvm.cpp

View File

@ -7,22 +7,25 @@
#include <android/log.h>
#include "skyline/loader/loader.h"
#include "skyline/common.h"
#include "skyline/common/settings.h"
#include "skyline/os.h"
#include "skyline/jvm.h"
#include "skyline/gpu.h"
#include "skyline/input.h"
#include "skyline/kernel/types/KProcess.h"
skyline::u16 fps;
skyline::u32 frametime;
std::weak_ptr<skyline::gpu::GPU> gpuWeak;
std::weak_ptr<skyline::input::Input> inputWeak;
skyline::u16 Fps;
skyline::u32 FrameTime;
std::weak_ptr<skyline::kernel::OS> OsWeak;
std::weak_ptr<skyline::gpu::GPU> GpuWeak;
std::weak_ptr<skyline::input::Input> InputWeak;
extern "C" JNIEXPORT void Java_emu_skyline_EmulationActivity_executeApplication(JNIEnv *env, jobject instance, jstring romUriJstring, jint romType, jint romFd, jint preferenceFd, jstring appFilesPathJstring) {
fps = 0;
frametime = 0;
Fps = 0;
FrameTime = 0;
pthread_setname_np(pthread_self(), "EmuMain");
setpriority(PRIO_PROCESS, static_cast<id_t>(gettid()), -8); // Set the priority of this process to the highest value
setpriority(PRIO_PGRP, static_cast<id_t>(gettid()), -8); // Set the priority of this process to the highest value
auto jvmManager{std::make_shared<skyline::JvmManager>(env, instance)};
auto settings{std::make_shared<skyline::Settings>(preferenceFd)};
@ -35,9 +38,10 @@ extern "C" JNIEXPORT void Java_emu_skyline_EmulationActivity_executeApplication(
auto start{std::chrono::steady_clock::now()};
try {
skyline::kernel::OS os(jvmManager, logger, settings, std::string(appFilesPath));
gpuWeak = os.state.gpu;
inputWeak = os.state.input;
auto os{std::make_shared<skyline::kernel::OS>(jvmManager, logger, settings, std::string(appFilesPath))};
OsWeak = os;
GpuWeak = os->state.gpu;
InputWeak = os->state.input;
jvmManager->InitializeControllers();
env->ReleaseStringUTFChars(appFilesPathJstring, appFilesPath);
@ -45,14 +49,14 @@ extern "C" JNIEXPORT void Java_emu_skyline_EmulationActivity_executeApplication(
logger->Info("Launching ROM {}", romUri);
env->ReleaseStringUTFChars(romUriJstring, romUri);
os.Execute(romFd, static_cast<skyline::loader::RomFormat>(romType));
os->Execute(romFd, static_cast<skyline::loader::RomFormat>(romType));
} catch (std::exception &e) {
logger->Error(e.what());
} catch (...) {
logger->Error("An unknown exception has occurred");
}
inputWeak.reset();
InputWeak.reset();
logger->Info("Emulation has ended");
@ -62,38 +66,50 @@ extern "C" JNIEXPORT void Java_emu_skyline_EmulationActivity_executeApplication(
close(romFd);
}
extern "C" JNIEXPORT void Java_emu_skyline_EmulationActivity_exitGuest(JNIEnv *, jobject, jboolean halt) {
// TODO
exit(0);
extern "C" JNIEXPORT void Java_emu_skyline_EmulationActivity_stopEmulation(JNIEnv *, jobject) {
auto os{OsWeak.lock()};
while (!os)
os = OsWeak.lock();
auto process{os->state.process};
while (!process) {
process = os->state.process;
__sync_synchronize();
}
while (!process->mainThread)
__sync_synchronize();
auto thread{process->mainThread}; // We just need to kill the main thread, it'll kill the rest itself
while (!thread->running)
__sync_synchronize();
thread->Kill(true);
}
extern "C" JNIEXPORT void Java_emu_skyline_EmulationActivity_setSurface(JNIEnv *, jobject, jobject surface) {
auto gpu{gpuWeak.lock()};
auto gpu{GpuWeak.lock()};
while (!gpu)
gpu = gpuWeak.lock();
gpu = GpuWeak.lock();
gpu->presentation.UpdateSurface(surface);
}
extern "C" JNIEXPORT jint Java_emu_skyline_EmulationActivity_getFps(JNIEnv *, jobject) {
return fps;
return Fps;
}
extern "C" JNIEXPORT jfloat Java_emu_skyline_EmulationActivity_getFrametime(JNIEnv *, jobject) {
return static_cast<float>(frametime) / 100;
return static_cast<float>(FrameTime) / 100;
}
extern "C" JNIEXPORT void JNICALL Java_emu_skyline_EmulationActivity_setController(JNIEnv *, jobject, jint index, jint type, jint partnerIndex) {
auto input{inputWeak.lock()};
auto input{InputWeak.lock()};
std::lock_guard guard(input->npad.mutex);
input->npad.controllers[index] = skyline::input::GuestController{static_cast<skyline::input::NpadControllerType>(type), static_cast<skyline::i8>(partnerIndex)};
}
extern "C" JNIEXPORT void JNICALL Java_emu_skyline_EmulationActivity_updateControllers(JNIEnv *, jobject) {
inputWeak.lock()->npad.Update();
InputWeak.lock()->npad.Update();
}
extern "C" JNIEXPORT void JNICALL Java_emu_skyline_EmulationActivity_setButtonState(JNIEnv *, jobject, jint index, jlong mask, jboolean pressed) {
auto input{inputWeak.lock()};
auto input{InputWeak.lock()};
if (!input)
return; // We don't mind if we miss button updates while input hasn't been initialized
auto device{input->npad.controllers[index].device};
@ -102,7 +118,7 @@ extern "C" JNIEXPORT void JNICALL Java_emu_skyline_EmulationActivity_setButtonSt
}
extern "C" JNIEXPORT void JNICALL Java_emu_skyline_EmulationActivity_setAxisValue(JNIEnv *, jobject, jint index, jint axis, jint value) {
auto input{inputWeak.lock()};
auto input{InputWeak.lock()};
if (!input)
return; // We don't mind if we miss axis updates while input hasn't been initialized
auto device{input->npad.controllers[index].device};
@ -113,7 +129,7 @@ extern "C" JNIEXPORT void JNICALL Java_emu_skyline_EmulationActivity_setAxisValu
extern "C" JNIEXPORT void JNICALL Java_emu_skyline_EmulationActivity_setTouchState(JNIEnv *env, jobject, jintArray pointsJni) {
using Point = skyline::input::TouchScreenPoint;
auto input{inputWeak.lock()};
auto input{InputWeak.lock()};
if (!input)
return; // We don't mind if we miss touch updates while input hasn't been initialized
jboolean isCopy{false};

View File

@ -16,6 +16,10 @@ namespace skyline::audio {
outputStream->requestStart();
}
Audio::~Audio() {
outputStream->requestStop();
}
std::shared_ptr<AudioTrack> Audio::OpenTrack(u8 channelCount, u32 sampleRate, const std::function<void()> &releaseCallback) {
std::lock_guard trackGuard(trackLock);

View File

@ -19,6 +19,8 @@ namespace skyline::audio {
public:
Audio(const DeviceState &state);
~Audio();
/**
* @brief Opens a new track that can be used to play sound
* @param channelCount The amount channels that are present in the track

View File

@ -4,7 +4,7 @@
#pragma once
#include <oboe/Oboe.h>
#include "circular_buffer.h"
#include <common.h>
namespace skyline {
namespace constant {

View File

@ -4,6 +4,7 @@
#pragma once
#include <kernel/types/KEvent.h>
#include <common/circular_buffer.h>
#include "common.h"
namespace skyline::audio {

View File

@ -1,7 +1,6 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
#include <tinyxml2.h>
#include <android/log.h>
#include "common.h"
#include "nce.h"
@ -11,63 +10,6 @@
#include "kernel/types/KThread.h"
namespace skyline {
Settings::Settings(int fd) {
tinyxml2::XMLDocument pref;
auto fileDeleter = [](FILE *file) { fclose(file); };
std::unique_ptr<FILE, decltype(fileDeleter)> file{fdopen(fd, "r"), fileDeleter};
if (pref.LoadFile(file.get()))
throw exception("TinyXML2 Error: " + std::string(pref.ErrorStr()));
tinyxml2::XMLElement *elem{pref.LastChild()->FirstChild()->ToElement()};
while (elem) {
switch (elem->Value()[0]) {
case 's':
stringMap[elem->FindAttribute("name")->Value()] = elem->GetText();
break;
case 'b':
boolMap[elem->FindAttribute("name")->Value()] = elem->FindAttribute("value")->BoolValue();
break;
case 'i':
intMap[elem->FindAttribute("name")->Value()] = elem->FindAttribute("value")->IntValue();
break;
default:
__android_log_print(ANDROID_LOG_WARN, "emu-cpp", "Settings type is missing: %s for %s", elem->Value(), elem->FindAttribute("name")->Value());
break;
};
if (elem->NextSibling())
elem = elem->NextSibling()->ToElement();
else
break;
}
pref.Clear();
}
std::string Settings::GetString(const std::string &key) {
return stringMap.at(key);
}
bool Settings::GetBool(const std::string &key) {
return boolMap.at(key);
}
int Settings::GetInt(const std::string &key) {
return intMap.at(key);
}
void Settings::List(const std::shared_ptr<Logger> &logger) {
for (auto &iter : stringMap)
logger->Info("Key: {}, Value: {}, Type: String", iter.first, GetString(iter.first));
for (auto &iter : boolMap)
logger->Info("Key: {}, Value: {}, Type: Bool", iter.first, GetBool(iter.first));
}
Logger::Logger(const std::string &path, LogLevel configLevel) : configLevel(configLevel) {
logFile.open(path, std::ios::trunc);
UpdateTag();
@ -95,6 +37,7 @@ namespace skyline {
std::lock_guard guard(mtx);
logFile << "0|" << str << "\n";
logFile.flush();
}
void Logger::Write(LogLevel level, std::string str) {
@ -112,10 +55,11 @@ namespace skyline {
std::lock_guard guard(mtx);
logFile << "1|" << levelCharacter[static_cast<u8>(level)] << '|' << threadName << '|' << str << '\n';
logFile.flush();
}
DeviceState::DeviceState(kernel::OS *os, std::shared_ptr<kernel::type::KProcess> &process, std::shared_ptr<JvmManager> jvmManager, std::shared_ptr<Settings> settings, std::shared_ptr<Logger> logger)
: os(os), jvm(std::move(jvmManager)), settings(std::move(settings)), logger(std::move(logger)), process(process) {
DeviceState::DeviceState(kernel::OS *os, std::shared_ptr<JvmManager> jvmManager, std::shared_ptr<Settings> settings, std::shared_ptr<Logger> logger)
: os(os), jvm(std::move(jvmManager)), settings(std::move(settings)), logger(std::move(logger)) {
// We assign these later as they use the state in their constructor and we don't want null pointers
nce = std::make_shared<nce::NCE>(*this);
gpu = std::make_shared<gpu::GPU>(*this);

View File

@ -141,7 +141,7 @@ namespace skyline {
template<class T>
size_t PointerValue(T *item) {
return reinterpret_cast<size_t>(item);
return reinterpret_cast<uintptr_t>(item);
}
/**
@ -440,48 +440,7 @@ namespace skyline {
}
};
/**
* @brief The Settings class is used to access the parameters set in the Java component of the application
*/
class Settings {
private:
std::unordered_map<std::string, std::string> stringMap; //!< A mapping from all keys to their corresponding string value
std::unordered_map<std::string, bool> boolMap; //!< A mapping from all keys to their corresponding boolean value
std::unordered_map<std::string, int> intMap; //!< A mapping from all keys to their corresponding integer value
public:
/**
* @param fd An FD to the preference XML file
*/
Settings(int fd);
/**
* @brief Retrieves a particular setting as a string
* @param key The key of the setting
* @return The string value of the setting
*/
std::string GetString(const std::string &key);
/**
* @brief Retrieves a particular setting as a boolean
* @param key The key of the setting
* @return The boolean value of the setting
*/
bool GetBool(const std::string &key);
/**
* @brief Retrieves a particular setting as a integer
* @param key The key of the setting
* @return The integer value of the setting
*/
int GetInt(const std::string &key);
/**
* @brief Writes all settings keys and values to syslog, this function is for development purposes
*/
void List(const std::shared_ptr<Logger> &logger);
};
class Settings;
namespace nce {
class NCE;
struct ThreadContext;
@ -511,19 +470,19 @@ namespace skyline {
* @brief The state of the entire emulator is contained within this class, all objects related to emulation are tied into it
*/
struct DeviceState {
DeviceState(kernel::OS *os, std::shared_ptr<kernel::type::KProcess> &process, std::shared_ptr<JvmManager> jvmManager, std::shared_ptr<Settings> settings, std::shared_ptr<Logger> logger);
DeviceState(kernel::OS *os, std::shared_ptr<JvmManager> jvmManager, std::shared_ptr<Settings> settings, std::shared_ptr<Logger> logger);
kernel::OS *os;
std::shared_ptr<kernel::type::KProcess> &process;
thread_local static std::shared_ptr<kernel::type::KThread> thread; //!< The KThread of the thread which accesses this object
thread_local static nce::ThreadContext *ctx; //!< The context of the guest thread for the corresponding host thread
std::shared_ptr<nce::NCE> nce;
std::shared_ptr<gpu::GPU> gpu;
std::shared_ptr<audio::Audio> audio;
std::shared_ptr<input::Input> input;
std::shared_ptr<loader::Loader> loader;
std::shared_ptr<JvmManager> jvm;
std::shared_ptr<Settings> settings;
std::shared_ptr<Logger> logger;
std::shared_ptr<loader::Loader> loader;
std::shared_ptr<gpu::GPU> gpu;
std::shared_ptr<audio::Audio> audio;
std::shared_ptr<input::Input> input;
std::shared_ptr<nce::NCE> nce;
std::shared_ptr<kernel::type::KProcess> process;
thread_local static std::shared_ptr<kernel::type::KThread> thread; //!< The KThread of the thread which accesses this object
thread_local static nce::ThreadContext *ctx; //!< The context of the guest thread for the corresponding host thread
};
}

View File

@ -5,7 +5,7 @@
#include <common.h>
namespace skyline::audio {
namespace skyline {
/**
* @brief An abstraction of an array into a circular buffer
* @tparam Type The type of elements stored in the buffer

View File

@ -5,9 +5,9 @@
#include <common.h>
namespace skyline::gpu {
namespace skyline {
/**
* @brief An efficient consumer-producer structure with internal synchronization
* @brief An efficient consumer-producer oriented queue with internal synchronization
*/
template<typename Type>
class CircularQueue {

View File

@ -0,0 +1,65 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
#include <android/log.h>
#include <tinyxml2.h>
#include "settings.h"
namespace skyline {
Settings::Settings(int fd) {
tinyxml2::XMLDocument pref;
auto fileDeleter = [](FILE *file) { fclose(file); };
std::unique_ptr<FILE, decltype(fileDeleter)> file{fdopen(fd, "r"), fileDeleter};
if (pref.LoadFile(file.get()))
throw exception("TinyXML2 Error: " + std::string(pref.ErrorStr()));
tinyxml2::XMLElement *elem{pref.LastChild()->FirstChild()->ToElement()};
while (elem) {
switch (elem->Value()[0]) {
case 's':
stringMap[elem->FindAttribute("name")->Value()] = elem->GetText();
break;
case 'b':
boolMap[elem->FindAttribute("name")->Value()] = elem->FindAttribute("value")->BoolValue();
break;
case 'i':
intMap[elem->FindAttribute("name")->Value()] = elem->FindAttribute("value")->IntValue();
break;
default:
__android_log_print(ANDROID_LOG_WARN, "emu-cpp", "Settings type is missing: %s for %s", elem->Value(), elem->FindAttribute("name")->Value());
break;
};
if (elem->NextSibling())
elem = elem->NextSibling()->ToElement();
else
break;
}
pref.Clear();
}
std::string Settings::GetString(const std::string &key) {
return stringMap.at(key);
}
bool Settings::GetBool(const std::string &key) {
return boolMap.at(key);
}
int Settings::GetInt(const std::string &key) {
return intMap.at(key);
}
void Settings::List(const std::shared_ptr<Logger> &logger) {
for (auto &iter : stringMap)
logger->Info("{} = \"{}\"", iter.first, GetString(iter.first));
for (auto &iter : boolMap)
logger->Info("{} = {}", iter.first, GetBool(iter.first));
}
}

View File

@ -0,0 +1,50 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
#pragma once
#include <common.h>
namespace skyline {
/**
* @brief The Settings class is used to access preferences set in the Kotlin component of Skyline
*/
class Settings {
private:
std::unordered_map<std::string, std::string> stringMap; //!< A mapping from all keys to their corresponding string value
std::unordered_map<std::string, bool> boolMap; //!< A mapping from all keys to their corresponding boolean value
std::unordered_map<std::string, int> intMap; //!< A mapping from all keys to their corresponding integer value
public:
/**
* @param fd An FD to the preference XML file
*/
Settings(int fd);
/**
* @brief Retrieves a particular setting as a string
* @param key The key of the setting
* @return The string value of the setting
*/
std::string GetString(const std::string &key);
/**
* @brief Retrieves a particular setting as a boolean
* @param key The key of the setting
* @return The boolean value of the setting
*/
bool GetBool(const std::string &key);
/**
* @brief Retrieves a particular setting as a integer
* @param key The key of the setting
* @return The integer value of the setting
*/
int GetInt(const std::string &key);
/**
* @brief Writes all settings keys and values to syslog, this function is for development purposes
*/
void List(const std::shared_ptr<Logger> &logger);
};
}

View File

@ -0,0 +1,136 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
#include <unistd.h>
#include <dlfcn.h>
#include <unwind.h>
#include <android/log.h>
#include "signal.h"
namespace skyline::signal {
thread_local SignalException signalException;
void ExceptionThrow() {
throw signalException;
}
void ExceptionalSignalHandler(int signal, siginfo *info, ucontext *context) {
signalException.signal = signal;
signalException.pc = context->uc_mcontext.pc;
if (signal == SIGSEGV)
signalException.faultAddress = info->si_addr;
context->uc_mcontext.pc = reinterpret_cast<u64>(&ExceptionThrow);
}
template<typename Signature>
Signature GetLibcFunction(const char *symbol) {
void *libc{dlopen("libc.so", RTLD_LOCAL | RTLD_LAZY)};
if (!libc)
throw exception("dlopen-ing libc has failed with: {}", dlerror());
auto function{reinterpret_cast<Signature>(dlsym(libc, symbol))};
if (!function)
throw exception("Cannot find '{}' in libc: {}", symbol, dlerror());
return function;
}
void Sigaction(int signal, const struct sigaction *action, struct sigaction *oldAction) {
static decltype(&sigaction) real{};
if (!real)
real = GetLibcFunction<decltype(&sigaction)>("sigaction");
if (real(signal, action, oldAction) < 0)
throw exception("sigaction has failed with {}", strerror(errno));
}
static void *(*TlsRestorer)(){};
void SetTlsRestorer(void *(*function)()) {
TlsRestorer = function;
}
std::array<void (*)(int, struct siginfo *, void *), NSIG> DefaultSignalHandlers;
struct ThreadSignalHandler {
pthread_key_t key;
std::atomic<u32> count;
void Decrement();
static void DecrementStatic(ThreadSignalHandler *thiz) {
thiz->Decrement();
}
};
std::array<ThreadSignalHandler, NSIG> ThreadSignalHandlers;
void ThreadSignalHandler::Decrement() {
u32 current;
while ((current = count.load()) && !count.compare_exchange_strong(current, --current));
if (current == 0) {
int signal{static_cast<int>(this - ThreadSignalHandlers.data())};
struct sigaction oldAction;
Sigaction(signal, nullptr, &oldAction);
struct sigaction action{
.sa_sigaction = DefaultSignalHandlers.at(signal),
.sa_flags = oldAction.sa_flags,
};
Sigaction(signal, &action);
}
}
void ThreadSignalHandler(int signal, siginfo *info, ucontext *context) {
void *tls{}; // The TLS value prior to being restored if it is
if (TlsRestorer)
tls = TlsRestorer();
auto handler{reinterpret_cast<void (*)(int, struct siginfo *, ucontext *, void *)>(pthread_getspecific(ThreadSignalHandlers.at(signal).key))};
if (handler) {
handler(signal, info, context, tls);
} else {
auto defaultHandler{DefaultSignalHandlers.at(signal)};
if (defaultHandler)
defaultHandler(signal, info, context);
}
if (tls)
asm volatile("MSR TPIDR_EL0, %x0"::"r"(tls));
}
void SetSignalHandler(std::initializer_list<int> signals, void (*function)(int, struct siginfo *, ucontext *, void *)) {
static std::array<std::once_flag, NSIG> signalHandlerOnce{};
stack_t stack;
sigaltstack(nullptr, &stack);
struct sigaction action{
.sa_sigaction = reinterpret_cast<void (*)(int, siginfo *, void *)>(ThreadSignalHandler),
.sa_flags = SA_RESTART | SA_SIGINFO | (stack.ss_sp && stack.ss_size ? SA_ONSTACK : 0),
};
for (int signal : signals) {
auto &threadHandler{ThreadSignalHandlers.at(signal)};
std::call_once(signalHandlerOnce[signal], [signal, action, &threadHandler]() {
if (int result = pthread_key_create(&threadHandler.key, reinterpret_cast<void (*)(void *)>(&ThreadSignalHandler::DecrementStatic)))
throw exception("Failed to create per-thread signal handler pthread key: {}", strerror(result));
struct sigaction oldAction;
Sigaction(signal, &action, &oldAction);
if (oldAction.sa_flags && oldAction.sa_flags != action.sa_flags)
throw exception("Old sigaction flags aren't equivalent to the replaced signal: {:#b} | {:#b}", oldAction.sa_flags, action.sa_flags);
DefaultSignalHandlers.at(signal) = oldAction.sa_sigaction;
});
if (!pthread_getspecific(ThreadSignalHandlers.at(signal).key))
threadHandler.count++;
pthread_setspecific(ThreadSignalHandlers.at(signal).key, reinterpret_cast<void *>(function));
}
}
void Sigprocmask(int how, const sigset_t &set, sigset_t *oldSet) {
static decltype(&pthread_sigmask) real{};
if (!real)
real = GetLibcFunction<decltype(&sigprocmask)>("sigprocmask");
if (real(how, &set, oldSet) < 0)
throw exception("sigprocmask has failed with {}", strerror(errno));
}
}

View File

@ -0,0 +1,66 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
#pragma once
#include <common.h>
namespace skyline::signal {
/**
* @brief An exception object that is designed specifically to hold Linux signals
* @note This doesn't inherit std::exception as it shouldn't be caught as such
* @note Refer to the manpage siginfo(3) for information on members
*/
class SignalException {
public:
int signal{};
u64 pc{};
void *faultAddress{};
inline std::string what() const {
if (!faultAddress)
return fmt::format("Signal: {} (PC: 0x{:X})", strsignal(signal), pc);
else
return fmt::format("Signal: {} @ 0x{:X} (PC: 0x{:X})", strsignal(signal), reinterpret_cast<u64>(faultAddress), pc);
}
};
/**
* @brief A signal handler which automatically throws an exception with the corresponding signal metadata in a SignalException
*/
void ExceptionalSignalHandler(int signal, siginfo *, ucontext *context);
/**
* @brief Our delegator for sigaction, we need to do this due to sigchain hooking bionic's sigaction and it intercepting signals before they're passed onto userspace
* This not only leads to performance degradation but also requires host TLS to be in the TLS register which we cannot ensure for in-guest signals
*/
void Sigaction(int signal, const struct sigaction *action, struct sigaction *oldAction = nullptr);
/**
* @brief If the TLS value of the code running prior to a signal has a custom TLS value, this should be used to restore it
* @param function A function which is inert if the TLS isn't required to be restored, it should return nullptr if TLS wasn't restored else the old TLS value
*/
void SetTlsRestorer(void *(*function)());
/**
* @brief A wrapper around Sigaction to make it easy to set a sigaction signal handler for multiple signals and also allow for thread-local signal handlers
* @param function A sa_action callback with the old TLS (If present) as the 4th argument
*/
void SetSignalHandler(std::initializer_list<int> signals, void (*function)(int, struct siginfo *, ucontext *, void *));
inline void SetSignalHandler(std::initializer_list<int> signals, void (*function)(int, struct siginfo *, ucontext *)) {
SetSignalHandler(signals, reinterpret_cast<void (*)(int, struct siginfo *, ucontext *, void *)>(function));
}
/**
* @brief Our delegator for sigprocmask, required due to libsigchain hooking this
*/
void Sigprocmask(int how, const sigset_t &set, sigset_t *oldSet = nullptr);
inline void BlockSignal(std::initializer_list<int> signals) {
sigset_t set{};
for (int signal : signals)
sigaddset(&set, signal);
Sigprocmask(SIG_BLOCK, set, nullptr);
}
}

View File

@ -24,8 +24,8 @@ namespace skyline::gpu {
std::shared_ptr<engine::Engine> maxwellCompute;
std::shared_ptr<engine::Engine> maxwellDma;
std::shared_ptr<engine::Engine> keplerMemory;
gpfifo::GPFIFO gpfifo;
std::array<Syncpoint, constant::MaxHwSyncpointCount> syncpoints{};
gpfifo::GPFIFO gpfifo;
inline GPU(const DeviceState &state) : state(state), presentation(state), memoryManager(state), gpfifo(state), fermi2D(std::make_shared<engine::Engine>(state)), keplerMemory(std::make_shared<engine::Engine>(state)), maxwell3D(std::make_shared<engine::Maxwell3D>(state)), maxwellCompute(std::make_shared<engine::Engine>(state)), maxwellDma(std::make_shared<engine::Engine>(state)) {}
};

View File

@ -1,6 +1,8 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
#include <common/signal.h>
#include <kernel/types/KProcess.h>
#include <gpu.h>
#include <gpu/engines/maxwell_3d.h>
#include "gpfifo.h"
@ -86,19 +88,38 @@ namespace skyline::gpu::gpfifo {
void GPFIFO::Run() {
pthread_setname_np(pthread_self(), "GPFIFO");
pushBuffers->Process([this](PushBuffer& pushBuffer){
if (pushBuffer.segment.empty())
pushBuffer.Fetch(state.gpu->memoryManager);
Process(pushBuffer.segment);
});
try {
signal::SetSignalHandler({SIGINT, SIGILL, SIGTRAP, SIGBUS, SIGFPE, SIGSEGV}, signal::ExceptionalSignalHandler);
pushBuffers->Process([this](PushBuffer &pushBuffer) {
if (pushBuffer.segment.empty())
pushBuffer.Fetch(state.gpu->memoryManager);
Process(pushBuffer.segment);
});
} catch (const signal::SignalException &e) {
if (e.signal != SIGINT) {
state.logger->Write(Logger::LogLevel::Error, e.what());
signal::BlockSignal({SIGINT});
state.process->mainThread->Kill(false);
}
} catch (const std::exception &e) {
state.logger->Write(Logger::LogLevel::Error, e.what());
state.process->mainThread->Kill(false);
}
}
void GPFIFO::Push(span<GpEntry> entries) {
bool beforeBarrier{true};
pushBuffers->AppendTranform(entries, [&beforeBarrier, this](const GpEntry& entry){
pushBuffers->AppendTranform(entries, [&beforeBarrier, this](const GpEntry &entry) {
if (entry.sync == GpEntry::Sync::Wait)
beforeBarrier = false;
return PushBuffer(entry, state.gpu->memoryManager, beforeBarrier);
});
}
GPFIFO::~GPFIFO() {
if (thread.joinable()) {
pthread_kill(thread.native_handle(), SIGINT);
thread.join();
}
}
}

View File

@ -3,7 +3,7 @@
#pragma once
#include "circular_queue.h"
#include <common/circular_queue.h>
#include "engines/gpfifo.h"
#include "memory_manager.h"
@ -163,6 +163,8 @@ namespace skyline::gpu {
public:
GPFIFO(const DeviceState &state) : state(state), gpfifoEngine(state) {}
~GPFIFO();
/**
* @param numBuffers The amount of push-buffers to allocate in the circular buffer
*/

View File

@ -5,8 +5,8 @@
#include "jvm.h"
#include "presentation_engine.h"
extern skyline::u16 fps;
extern skyline::u32 frametime;
extern skyline::u16 Fps;
extern skyline::u32 FrameTime;
namespace skyline::gpu {
PresentationEngine::PresentationEngine(const DeviceState &state) : state(state), vsyncEvent(std::make_shared<kernel::type::KEvent>(state)), bufferEvent(std::make_shared<kernel::type::KEvent>(state)) {}
@ -29,7 +29,7 @@ namespace skyline::gpu {
if (!env->IsSameObject(newSurface, nullptr))
surface = env->NewGlobalRef(newSurface);
if (surface) {
window = ANativeWindow_fromSurface(state.jvm->GetEnv(), surface);
window = ANativeWindow_fromSurface(env, surface);
ANativeWindow_acquire(window);
resolution.width = static_cast<u32>(ANativeWindow_getWidth(window));
resolution.height = static_cast<u32>(ANativeWindow_getHeight(window));
@ -73,8 +73,8 @@ namespace skyline::gpu {
if (frameTimestamp) {
auto now{util::GetTimeNs()};
frametime = static_cast<u32>((now - frameTimestamp) / 10000); // frametime / 100 is the real ms value, this is to retain the first two decimals
fps = static_cast<u16>(constant::NsInSecond / (now - frameTimestamp));
FrameTime = static_cast<u32>((now - frameTimestamp) / 10000); // frametime / 100 is the real ms value, this is to retain the first two decimals
Fps = static_cast<u16>(constant::NsInSecond / (now - frameTimestamp));
frameTimestamp = now;
} else {

View File

@ -14,7 +14,7 @@ namespace skyline::gpu {
const DeviceState &state;
std::mutex windowMutex;
std::condition_variable windowConditional;
jobject surface; //!< The Surface object backing the ANativeWindow
jobject surface{}; //!< The Surface object backing the ANativeWindow
u64 frameTimestamp{}; //!< The timestamp of the last frame being shown
public:

View File

@ -8,18 +8,19 @@ namespace skyline {
* @brief A thread-local wrapper over JNIEnv and JavaVM which automatically handles attaching and detaching threads
*/
struct JniEnvironment {
static inline JNIEnv *env{};
JNIEnv *env{};
static inline JavaVM *vm{};
bool attached{};
JniEnvironment(JNIEnv *environment) {
void Initialize(JNIEnv *environment) {
env = environment;
if (env->GetJavaVM(&vm) < 0)
throw exception("Cannot get JavaVM from environment");
attached = true;
}
JniEnvironment() {
if (vm) {
if (vm && !attached) {
vm->AttachCurrentThread(&env, nullptr);
attached = true;
}
@ -31,10 +32,14 @@ namespace skyline {
}
operator JNIEnv *() {
if (!attached)
throw exception("Not attached");
return env;
}
JNIEnv* operator->() {
JNIEnv *operator->() {
if (!attached)
throw exception("Not attached");
return env;
}
};
@ -42,7 +47,7 @@ namespace skyline {
thread_local inline JniEnvironment env;
JvmManager::JvmManager(JNIEnv *environ, jobject instance) : instance(environ->NewGlobalRef(instance)), instanceClass(reinterpret_cast<jclass>(environ->NewGlobalRef(environ->GetObjectClass(instance)))), initializeControllersId(environ->GetMethodID(instanceClass, "initializeControllers", "()V")), vibrateDeviceId(environ->GetMethodID(instanceClass, "vibrateDevice", "(I[J[I)V")), clearVibrationDeviceId(environ->GetMethodID(instanceClass, "clearVibrationDevice", "(I)V")) {
env = JniEnvironment(environ);
env.Initialize(environ);
}
JvmManager::~JvmManager() {

View File

@ -7,6 +7,11 @@
namespace skyline::kernel {
MemoryManager::MemoryManager(const DeviceState &state) : state(state) {}
MemoryManager::~MemoryManager() {
if (base.address && base.size)
munmap(reinterpret_cast<void *>(base.address), base.size);
}
constexpr size_t RegionAlignment{1ULL << 21}; //!< The minimum alignment of a HOS memory region
void MemoryManager::InitializeVmm(memory::AddressSpaceType type) {

View File

@ -226,6 +226,8 @@ namespace skyline {
MemoryManager(const DeviceState &state);
~MemoryManager();
/**
* @note This should be called before any mappings in the VMM or calls to InitalizeRegions are done
*/

View File

@ -169,7 +169,7 @@ namespace skyline::kernel::svc {
std::memcpy(source, destination, size);
auto sourceObject{state.process->GetMemoryObject(destination)};
auto sourceObject{state.process->GetMemoryObject(source)};
if (!sourceObject)
throw exception("svcUnmapMemory: Cannot find source memory object in handle table for address 0x{:X}", source);
@ -216,7 +216,9 @@ namespace skyline::kernel::svc {
void ExitProcess(const DeviceState &state) {
state.logger->Debug("svcExitProcess: Exiting process");
exit(0);
if (state.thread->id)
state.process->mainThread->Kill(false);
std::longjmp(state.thread->originalCtx, true);
}
constexpr i32 IdealCoreDontCare{-1};
@ -269,8 +271,7 @@ namespace skyline::kernel::svc {
void ExitThread(const DeviceState &state) {
state.logger->Debug("svcExitThread: Exiting current thread: {}", state.thread->id);
state.thread->Kill();
pthread_exit(nullptr);
std::longjmp(state.thread->originalCtx, true);
}
void SleepThread(const DeviceState &state) {
@ -708,34 +709,34 @@ namespace skyline::kernel::svc {
void GetInfo(const DeviceState &state) {
enum class InfoState : u32 {
// 1.0.0+
AllowedCpuIdBitmask = 0,
AllowedThreadPriorityMask = 1,
AliasRegionBaseAddr = 2,
AliasRegionSize = 3,
HeapRegionBaseAddr = 4,
HeapRegionSize = 5,
TotalMemoryAvailable = 6,
TotalMemoryUsage = 7,
IsCurrentProcessBeingDebugged = 8,
ResourceLimit = 9,
IdleTickCount = 10,
RandomEntropy = 11,
AllowedCpuIdBitmask = 0,
AllowedThreadPriorityMask = 1,
AliasRegionBaseAddr = 2,
AliasRegionSize = 3,
HeapRegionBaseAddr = 4,
HeapRegionSize = 5,
TotalMemoryAvailable = 6,
TotalMemoryUsage = 7,
IsCurrentProcessBeingDebugged = 8,
ResourceLimit = 9,
IdleTickCount = 10,
RandomEntropy = 11,
// 2.0.0+
AddressSpaceBaseAddr = 12,
AddressSpaceSize = 13,
StackRegionBaseAddr = 14,
StackRegionSize = 15,
AddressSpaceBaseAddr = 12,
AddressSpaceSize = 13,
StackRegionBaseAddr = 14,
StackRegionSize = 15,
// 3.0.0+
TotalSystemResourceAvailable = 16,
TotalSystemResourceUsage = 17,
TitleId = 18,
TotalSystemResourceAvailable = 16,
TotalSystemResourceUsage = 17,
TitleId = 18,
// 4.0.0+
PrivilegedProcessId = 19,
PrivilegedProcessId = 19,
// 5.0.0+
UserExceptionContextAddr = 20,
UserExceptionContextAddr = 20,
// 6.0.0+
TotalMemoryAvailableWithoutSystemResource = 21,
TotalMemoryUsageWithoutSystemResource = 22,
TotalMemoryUsageWithoutSystemResource = 22,
};
InfoState info{static_cast<u32>(state.ctx->gpr.w1)};

View File

@ -50,14 +50,15 @@ namespace skyline::kernel::type {
}
std::shared_ptr<KThread> KProcess::CreateThread(void *entry, u64 argument, void *stackTop, i8 priority, i8 idealCore) {
if (!stackTop && threads.empty()) { //!< Main thread stack is created by the kernel and owned by the process
if (!stackTop && !mainThread) { //!< Main thread stack is created by the kernel and owned by the process
mainThreadStack = mainThreadStack.make_shared(state, reinterpret_cast<u8 *>(state.process->memory.stack.address), state.process->npdm.meta.mainThreadStackSize, memory::Permission{true, true, false}, memory::states::Stack);
if (mprotect(mainThreadStack->ptr, PAGE_SIZE, PROT_NONE))
throw exception("Failed to create guard page for thread stack at 0x{:X}", mainThreadStack->ptr);
stackTop = mainThreadStack->ptr + mainThreadStack->size;
}
auto thread{NewHandle<KThread>(this, threads.size(), entry, argument, stackTop, (priority == -1) ? state.process->npdm.meta.mainThreadPriority : priority, (idealCore == -1) ? state.process->npdm.meta.idealCore : idealCore).item};
threads.push_back(thread);
auto thread{NewHandle<KThread>(this, threadIndex++, entry, argument, stackTop, (priority == -1) ? state.process->npdm.meta.mainThreadPriority : priority, (idealCore == -1) ? state.process->npdm.meta.idealCore : idealCore).item};
if (!mainThread)
mainThread = thread;
return thread;
}

View File

@ -24,13 +24,10 @@ namespace skyline {
* @brief KProcess manages process-global state such as memory, kernel handles allocated to the process and synchronization primitives
*/
class KProcess : public KSyncObject {
public:
MemoryManager memory; // This is here to ensure it is present during the destruction of dependent objects
public: // We have intermittent public/private members to ensure proper construction/destruction order
MemoryManager memory;
private:
std::vector<std::shared_ptr<KObject>> handles;
std::shared_mutex handleMutex;
struct WaitStatus {
std::atomic_bool flag{false};
i8 priority;
@ -47,6 +44,8 @@ namespace skyline {
std::mutex mutexLock;
std::mutex conditionalLock;
size_t threadIndex{}; //!< The ID assigned to the next created thread
/**
* @brief The status of a single TLS page (A page is 4096 bytes on ARMv8)
* Each TLS page has 8 slots, each 0x200 (512) bytes in size
@ -69,10 +68,15 @@ namespace skyline {
public:
std::shared_ptr<KPrivateMemory> mainThreadStack;
std::shared_ptr<KPrivateMemory> heap;
std::vector<std::shared_ptr<KThread>> threads;
std::vector<std::shared_ptr<TlsPage>> tlsPages;
std::shared_ptr<KThread> mainThread;
vfs::NPDM npdm;
private:
std::shared_mutex handleMutex;
std::vector<std::shared_ptr<KObject>> handles;
public:
KProcess(const DeviceState &state);
/**

View File

@ -64,7 +64,7 @@ namespace skyline::kernel::type {
if (kernel.Valid())
munmap(kernel.ptr, kernel.size);
if (guest.Valid()) {
if (state.process && guest.Valid()) {
mmap(guest.ptr, guest.size, PROT_NONE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
state.process->memory.InsertChunk(ChunkDescriptor{
.ptr = guest.ptr,

View File

@ -2,8 +2,8 @@
// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
#include <unistd.h>
#include <dlfcn.h>
#include <android/log.h>
#include <common/signal.h>
#include <nce.h>
#include <os.h>
#include "KProcess.h"
@ -15,29 +15,19 @@ namespace skyline::kernel::type {
}
KThread::~KThread() {
Kill();
}
/**
* @brief Our delegator for sigaction, we need to do this due to sigchain hooking bionic's sigaction and it intercepting signals before they're passed onto userspace
* This not only leads to performance degradation but also requires host TLS to be in the TLS register which we cannot ensure for in-guest signals
*/
inline void Sigaction(int signal, const struct sigaction &action, struct sigaction *oldAction = nullptr) {
static decltype(&sigaction) realSigaction{};
if (!realSigaction) {
void *libc{dlopen("libc.so", RTLD_LOCAL | RTLD_LAZY)};
if (!libc)
throw exception("dlopen-ing libc has failed with: {}", dlerror());
realSigaction = reinterpret_cast<decltype(&sigaction)>(dlsym(libc, "sigaction"));
if (!realSigaction)
throw exception("Cannot find 'sigaction' in libc: {}", dlerror());
if (running && pthread != pthread_self()) {
pthread_kill(pthread, SIGINT);
if (thread)
thread->join();
else
pthread_join(pthread, nullptr);
}
if (realSigaction(signal, &action, oldAction) < 0)
throw exception("sigaction has failed with {}", strerror(errno));
}
void KThread::StartThread() {
pthread_setname_np(pthread_self(), fmt::format("HOS-{}", id).c_str());
std::array<char, 16> threadName;
pthread_getname_np(pthread, threadName.data(), threadName.size());
pthread_setname_np(pthread, fmt::format("HOS-{}", id).c_str());
state.logger->UpdateTag();
if (!ctx.tpidrroEl0)
@ -47,12 +37,17 @@ namespace skyline::kernel::type {
state.ctx = &ctx;
state.thread = shared_from_this();
struct sigaction sigact{
.sa_sigaction = &nce::NCE::SignalHandler,
.sa_flags = SA_SIGINFO,
};
for (int signal : {SIGILL, SIGTRAP, SIGBUS, SIGFPE, SIGSEGV})
Sigaction(signal, sigact);
if (setjmp(originalCtx)) { // Returns 1 if it's returning from guest, 0 otherwise
running = false;
Signal();
pthread_setname_np(pthread, threadName.data());
state.logger->UpdateTag();
return;
}
signal::SetSignalHandler({SIGINT, SIGILL, SIGTRAP, SIGBUS, SIGFPE, SIGSEGV}, nce::NCE::SignalHandler);
asm volatile(
"MRS X0, TPIDR_EL0\n\t"
@ -136,28 +131,32 @@ namespace skyline::kernel::type {
}
void KThread::Start(bool self) {
std::unique_lock lock(mutex);
if (!running) {
running = true;
state.logger->Debug("Starting thread #{}", id);
if (self)
if (self) {
pthread = pthread_self();
lock.unlock();
StartThread();
else
} else {
thread.emplace(&KThread::StartThread, this);
pthread = thread->native_handle();
}
}
}
void KThread::Kill() {
void KThread::Kill(bool join) {
std::lock_guard lock(mutex);
if (running) {
pthread_kill(pthread, SIGINT);
if (join)
pthread_join(pthread, nullptr);
running = false;
Signal();
}
}
void KThread::UpdatePriority(i8 priority) {
this->priority = priority;
auto priorityValue{constant::AndroidPriority.Rescale(constant::HosPriority, priority)};
if (setpriority(PRIO_PROCESS, getpid(), priorityValue) == -1)
throw exception("Couldn't set thread priority to {} for #{}", priorityValue, id);
}
}

View File

@ -3,6 +3,7 @@
#pragma once
#include <csetjmp>
#include <nce/guest.h>
#include "KSyncObject.h"
#include "KPrivateMemory.h"
@ -35,7 +36,6 @@ namespace skyline {
namespace constant {
constexpr u8 CoreCount{4}; // The amount of cores an HOS process can be scheduled onto (User applications can only be on the first 3 cores, the last one is reserved for the system)
constexpr kernel::type::Priority AndroidPriority{19, -8}; //!< The range of priorities for Android
constexpr kernel::type::Priority HosPriority{0, 63}; //!< The range of priorities for Horizon OS
}
@ -43,25 +43,28 @@ namespace skyline {
/**
* @brief KThread manages a single thread of execution which is responsible for running guest code and kernel code which is invoked by the guest
*/
class KThread : public KSyncObject, public std::enable_shared_from_this<KThread> {
class KThread : public KSyncObject, public std::enable_shared_from_this<KThread> {
private:
KProcess *parent;
std::optional<std::thread> thread; //!< If this KThread is backed by a host thread then this'll hold it
pthread_t pthread{}; //!< The pthread_t for the host thread running this guest thread
void StartThread();
public:
std::mutex mutex; //!< Synchronizes all thread state changes
bool running{false};
std::atomic<bool> cancelSync{false}; //!< This is to flag to a thread to cancel a synchronization call it currently is in
KHandle handle;
size_t id; //!< Index of thread in parent process's KThread vector
nce::ThreadContext ctx{};
nce::ThreadContext ctx{}; //!< The context of the guest thread during the last SVC
jmp_buf originalCtx; //!< The context of the host thread prior to jumping into guest code
void* entry;
void *entry;
u64 entryArgument;
void* stackTop;
void *stackTop;
i8 priority;
i8 idealCore;
@ -80,15 +83,10 @@ namespace skyline {
void Start(bool self = false);
/**
* @brief Updates the internal state of the thread to signal it being dead
* @note This should only be called by the host thread running this guest thread
* @param join Returns after the guest thread has joined rather than instantly
*/
void Kill();
void Kill(bool join);
/**
* @brief Sets the host priority using setpriority with a rescaled the priority from HOS to Android
* @note It also affects guest scheduler behavior, this isn't purely for host
*/
void UpdatePriority(i8 priority);
};
}

View File

@ -1,8 +1,8 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
#include <sched.h>
#include <unistd.h>
#include "common/signal.h"
#include "os.h"
#include "gpu.h"
#include "jvm.h"
@ -23,57 +23,84 @@ namespace skyline::nce {
} else {
throw exception("Unimplemented SVC 0x{:X}", svc);
}
} catch (const signal::SignalException &e) {
if (e.signal != SIGINT) {
state.logger->Error("{} (SVC: 0x{:X})", e.what(), svc);
if (state.thread->id) {
signal::BlockSignal({SIGINT});
state.process->mainThread->Kill(false);
}
}
std::longjmp(state.thread->originalCtx, true);
} catch (const std::exception &e) {
state.logger->Error("{} (SVC: 0x{:X})", e.what(), svc);
exit(0);
if (state.thread->id) {
signal::BlockSignal({SIGINT});
state.process->mainThread->Kill(false);
}
std::longjmp(state.thread->originalCtx, true);
}
}
void NCE::SignalHandler(int signal, siginfo *, void *context) {
void NCE::SignalHandler(int signal, siginfo *info, ucontext *context, void *oldTls) {
if (oldTls) {
const auto &state{*reinterpret_cast<ThreadContext *>(oldTls)->state};
state.logger->Warn("Thread #{} has crashed due to signal: {}", state.thread->id, strsignal(signal));
std::string raw;
std::string trace;
std::string cpuContext;
const auto &ctx{reinterpret_cast<ucontext *>(context)->uc_mcontext};
constexpr u16 instructionCount{20}; // The amount of previous instructions to print
auto offset{ctx.pc - (instructionCount * sizeof(u32)) + (2 * sizeof(u32))};
span instructions(reinterpret_cast<u32 *>(offset), instructionCount);
for (auto &instruction : instructions) {
instruction = __builtin_bswap32(instruction);
if (offset == ctx.pc)
trace += fmt::format("\n-> 0x{:X} : 0x{:08X}", offset, instruction);
else
trace += fmt::format("\n 0x{:X} : 0x{:08X}", offset, instruction);
raw += fmt::format("{:08X}", instruction);
offset += sizeof(u32);
}
if (ctx.fault_address)
cpuContext += fmt::format("\nFault Address: 0x{:X}", ctx.fault_address);
if (ctx.sp)
cpuContext += fmt::format("\nStack Pointer: 0x{:X}", ctx.sp);
for (u8 index{}; index < ((sizeof(mcontext_t::regs) / sizeof(u64)) - 2); index += 2)
cpuContext += fmt::format("\n{}X{}: 0x{:<16X} {}{}: 0x{:X}", index < 10 ? ' ' : '\0', index, ctx.regs[index], index < 10 ? ' ' : '\0', index + 1, ctx.regs[index]);
state.logger->Debug("Process Trace:{}", trace);
state.logger->Debug("Raw Instructions: 0x{}", raw);
state.logger->Debug("CPU Context:{}", cpuContext);
context->uc_mcontext.pc = reinterpret_cast<skyline::u64>(&std::longjmp);
context->uc_mcontext.regs[0] = reinterpret_cast<u64>(state.thread->originalCtx);
context->uc_mcontext.regs[1] = true;
} else {
signal::ExceptionalSignalHandler(signal, info, context); //!< Delegate throwing a host exception to the exceptional signal handler
}
}
void *NceTlsRestorer() {
ThreadContext *threadCtx;
asm volatile("MRS %x0, TPIDR_EL0":"=r"(threadCtx));
if (threadCtx->magic != constant::SkyTlsMagic)
return nullptr;
asm volatile("MSR TPIDR_EL0, %x0"::"r"(threadCtx->hostTpidrEl0));
const auto &state{*threadCtx->state};
state.logger->Warn("Thread #{} has crashed due to signal: {}", state.thread->id, strsignal(signal));
std::string raw;
std::string trace;
std::string cpuContext;
const auto &ctx{reinterpret_cast<ucontext *>(context)->uc_mcontext};
constexpr u16 instructionCount{20}; // The amount of previous instructions to print
auto offset{ctx.pc - (instructionCount * sizeof(u32)) + (2 * sizeof(u32))};
span instructions(reinterpret_cast<u32 *>(offset), instructionCount);
for (auto &instruction : instructions) {
instruction = __builtin_bswap32(instruction);
if (offset == ctx.pc)
trace += fmt::format("\n-> 0x{:X} : 0x{:08X}", offset, instruction);
else
trace += fmt::format("\n 0x{:X} : 0x{:08X}", offset, instruction);
raw += fmt::format("{:08X}", instruction);
offset += sizeof(u32);
}
if (ctx.fault_address)
cpuContext += fmt::format("\nFault Address: 0x{:X}", ctx.fault_address);
if (ctx.sp)
cpuContext += fmt::format("\nStack Pointer: 0x{:X}", ctx.sp);
for (u8 index{}; index < ((sizeof(mcontext_t::regs) / sizeof(u64)) - 2); index += 2)
cpuContext += fmt::format("\n{}X{}: 0x{:<16X} {}{}: 0x{:X}", index < 10 ? ' ' : '\0', index, ctx.regs[index], index < 10 ? ' ' : '\0', index + 1, ctx.regs[index]);
state.logger->Warn("Process Trace:{}", trace);
state.logger->Warn("Raw Instructions: 0x{}", raw);
state.logger->Warn("CPU Context:{}", cpuContext);
asm volatile("MSR TPIDR_EL0, %x0"::"r"(threadCtx));
return threadCtx;
}
NCE::NCE(DeviceState &state) : state(state) {}
NCE::NCE(const DeviceState &state) : state(state) {
signal::SetTlsRestorer(&NceTlsRestorer);
}
constexpr u8 MainSvcTrampolineSize{17}; // Size of the main SVC trampoline function in u32 units
constexpr u32 TpidrEl0{0x5E82}; // ID of TPIDR_EL0 in MRS

View File

@ -8,18 +8,18 @@
namespace skyline::nce {
/**
* @brief The NCE (Native Code Execution) class is responsible for managing the state of catching instructions and directly controlling processes/threads
* @brief The NCE (Native Code Execution) class is responsible for managing state relevant to the layer between the host and guest
*/
class NCE {
private:
DeviceState &state;
const DeviceState &state;
static void SvcHandler(u16 svc, ThreadContext* ctx);
public:
static void SignalHandler(int signal, siginfo *info, void *context);
static void SignalHandler(int signal, siginfo *info, ucontext *context, void *oldTls);
NCE(DeviceState &state);
NCE(const DeviceState &state);
struct PatchData {
size_t size; //!< Size of the .patch section

View File

@ -7,6 +7,9 @@
namespace skyline {
struct DeviceState;
namespace constant {
constexpr u64 SkyTlsMagic{util::MakeMagic<u64>("SKYTLS")};
}
namespace nce {
struct WRegister {
u32 lower;
@ -96,6 +99,7 @@ namespace skyline {
u8 *tpidrroEl0; //!< Emulated HOS TPIDRRO_EL0
u8 *tpidrEl0; //!< Emulated HOS TPIDR_EL0
const DeviceState *state;
u64 magic{constant::SkyTlsMagic};
};
namespace guest {

View File

@ -12,7 +12,7 @@
#include "os.h"
namespace skyline::kernel {
OS::OS(std::shared_ptr<JvmManager> &jvmManager, std::shared_ptr<Logger> &logger, std::shared_ptr<Settings> &settings, const std::string &appFilesPath) : state(this, process, jvmManager, settings, logger), serviceManager(state), appFilesPath(appFilesPath) {}
OS::OS(std::shared_ptr<JvmManager> &jvmManager, std::shared_ptr<Logger> &logger, std::shared_ptr<Settings> &settings, const std::string &appFilesPath) : state(this, jvmManager, settings, logger), serviceManager(state), appFilesPath(appFilesPath) {}
void OS::Execute(int romFd, loader::RomFormat romType) {
auto romFile{std::make_shared<vfs::OsBacking>(romFd)};
@ -29,6 +29,7 @@ namespace skyline::kernel {
else
throw exception("Unsupported ROM extension.");
auto& process{state.process};
process = std::make_shared<kernel::type::KProcess>(state);
auto entry{state.loader->LoadProcessData(process, state)};
process->InitializeHeap();

View File

@ -13,7 +13,6 @@ namespace skyline::kernel {
class OS {
public:
DeviceState state;
std::shared_ptr<type::KProcess> process;
service::ServiceManager serviceManager;
std::string appFilesPath; //!< The full path to the app's files directory

View File

@ -1,6 +1,7 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
#include <common/settings.h>
#include "IProfile.h"
namespace skyline::service::account {

View File

@ -1,6 +1,7 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
#include <common/settings.h>
#include <kernel/types/KProcess.h>
#include "ICommonStateGetter.h"

View File

@ -68,11 +68,9 @@ class EmulationActivity : AppCompatActivity(), SurfaceHolder.Callback, View.OnTo
private external fun executeApplication(romUri : String, romType : Int, romFd : Int, preferenceFd : Int, appFilesPath : String)
/**
* This sets the halt flag in libskyline to the provided value, if set to true it causes libskyline to halt emulation
*
* @param halt The value to set halt to
* Terminate of all emulator threads and cause [emulationThread] to return
*/
private external fun exitGuest(halt : Boolean)
private external fun stopEmulation()
/**
* This sets the surface object in libskyline to the provided value, emulation is halted if set to null
@ -233,31 +231,26 @@ class EmulationActivity : AppCompatActivity(), SurfaceHolder.Callback, View.OnTo
* This is used to stop the currently executing ROM and replace it with the one specified in the new intent
*/
override fun onNewIntent(intent : Intent?) {
super.onNewIntent(intent)
shouldFinish = false
exitGuest(true)
stopEmulation()
emulationThread.join()
shouldFinish = true
executeApplication(intent?.data!!)
super.onNewIntent(intent)
}
/**
* This is used to halt emulation entirely
*/
override fun onDestroy() {
super.onDestroy()
shouldFinish = false
exitGuest(true)
emulationThread.join(1000)
stopEmulation()
emulationThread.join()
vibrators.forEach { (_, vibrator) -> vibrator.cancel() }
vibrators.clear()
super.onDestroy()
}
/**