Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 23 additions & 3 deletions jmap_dumper/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,10 @@ pub struct DumpOptions {
pub names: bool,
/// Print struct layouts before dumping
pub verbose: bool,
/// Skip vtable analysis
pub skip_vtables: bool,
/// Skip all objects
pub skip_objects: bool,
}

pub fn dump(
Expand Down Expand Up @@ -752,6 +756,14 @@ async fn dump_one(
return Ok(None);
};

if options.verbose {
eprintln!("[{i}/{num}] {0:#x}", obj.address());
}

if obj.vtable().address() == 0 {
return Ok(None);
}

let path = obj.path().await?;

if options.verbose {
Expand All @@ -769,7 +781,11 @@ async fn dump_inner(mem: Ctx, source_name: &str, options: DumpOptions) -> Result
let mut objects = BTreeMap::<String, ObjectType>::default();
let mut child_map = HashMap::<String, BTreeSet<String>>::default();

let num = uobjectarray.num_elements().await? as usize;
let num = if !options.skip_objects {
uobjectarray.num_elements().await? as usize
} else {
0
};

// keep many object dumps in flight
let mut stream = futures_util::stream::iter(0..num)
Expand Down Expand Up @@ -800,8 +816,12 @@ async fn dump_inner(mem: Ctx, source_name: &str, options: DumpOptions) -> Result
}
}

let vtables = vtable::analyze_vtables(&mem, &mut objects).await;

let vtables = if options.skip_vtables {
BTreeMap::new()
} else {
vtable::analyze_vtables(&mem, &mut objects).await
};

let names = if options.names {
Some(extract_fnames(&mem).await?)
} else {
Expand Down
10 changes: 10 additions & 0 deletions jmap_dumper/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,14 @@ struct Cli {
#[arg(long, short = 'v')]
verbose: bool,

/// Skip vtables
#[arg(long)]
skip_vtables: bool,

/// Skip objects
#[arg(long)]
skip_objects: bool,

/// Output dump .jmap path
#[arg(index = 1)]
output: PathBuf,
Expand Down Expand Up @@ -129,6 +137,8 @@ fn main() -> Result<()> {
all: cli.all,
names: cli.names,
verbose: cli.verbose,
skip_vtables: cli.skip_vtables,
skip_objects: cli.skip_objects,
};

let overrides = ConfigOverrides {
Expand Down
1 change: 1 addition & 0 deletions jmap_dumper/src/structs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ pub fn get_struct_info_for_version(
("properties", "FField"),
("properties", "FFieldClass"),
("properties", "FFieldVariant"),
("package", "UPackage"),
];

let mounted_container = vm_state.mount_container(container)?;
Expand Down
15 changes: 13 additions & 2 deletions jmap_dumper/unreal/src/objects.gs
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,12 @@ class UClass : UStruct,
uint32_t ClassUnique : 1;
uint32_t bCooked : 1;
}
if (UE_VERSION >= 508) {
bool bNeedsDynamicSubobjectInstancing;
bool bNeedsPostLoadSubobjectInstancing;
}

if (UE_VERSION >= 508) int32_t PropertiesStartOffset;
EClassFlags ClassFlags;
EClassCastFlags ClassCastFlags;
if (UE_VERSION < 408) int32_t ClassUnique;
Expand All @@ -227,7 +233,7 @@ class UClass : UStruct,
STUB* SparseClassData;
UScriptStruct* SparseClassDataStruct;
}
if (UE_VERSION >= 507) bool bNeedsDynamicSubobjectInstancing;
if (UE_VERSION == 507) bool bNeedsDynamicSubobjectInstancing;
TMap<FName, UFunction*> FuncMap;
if (UE_VERSION >= 411 && UE_VERSION < 418) {
TMap<FName, UFunction*> ParentFuncMap;
Expand All @@ -238,10 +244,14 @@ class UClass : UStruct,
TMap<FName, UFunction*> SuperFuncMap;
if (UE_VERSION >= 421) FWindowsRWLock SuperFuncMapLock;
}
if (UE_VERSION >= 503) {
if (UE_VERSION >= 503 && UE_VERSION < 508) {
TMap<FName, UFunction*> AllFunctionsCache;
FWindowsRWLock AllFunctionsCacheLock;
} else if (UE_VERSION >= 508) {
FWindowsRWLock AllFunctionsCacheLock;
TMap<FName, UFunction*> AllFunctionsCache;
}

TArray<FImplementedInterface> Interfaces;
if (UE_VERSION >= 503) STUB* ReferenceSchema; // UE::GC::FSchemaOwner
else if (UE_VERSION == 502) FTokenStreamOwner ReferenceTokens; // UE::GC::FTokenStreamOwner
Expand All @@ -250,4 +260,5 @@ class UClass : UStruct,
if (UE_VERSION >= 415) FCriticalSection ReferenceTokenStreamCritical;
}
TArray<FNativeFunctionLookup> NativeFunctionLookupTable;
if (UE_VERSION >= 508) STUB* Partials; // UE::CoreUObject::Private::FPartialClass
};
1 change: 1 addition & 0 deletions jmap_dumper/unreal/src/properties.gs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ struct TObjectPtr {

/// TEST
class FFieldClass {
if (UE_VERSION >= 508) uint64_t VTable;
FName Name;
if (UE_VERSION >= 507) EClassFlags ClassFlags;
uint64_t Id;
Expand Down
29 changes: 22 additions & 7 deletions jmap_dumper/unreal/src/uobjectarray.gs
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,10 @@ struct member_pack(if (UE_PACK_FUOBJECT_ITEM) 4 else 8) FUObjectItem {
if (UE_VERSION < 413) int32_t ClusterAndFlags;
if (UE_VERSION >= 413 && UE_VERSION < 507) int32_t Flags;
if (UE_VERSION >= 413 && UE_VERSION < 416) int32_t ClusterIndex;
if (UE_VERSION >= 416) int32_t ClusterRootIndex;
if (UE_VERSION >= 416 && UE_VERSION < 507) int32_t ClusterRootIndex;

int32_t SerialNumber;
if (UE_VERSION >= 507) int32_t ClusterRootIndex;
if (UE_VERSION >= 505 && UE_VERSION < 507) int32_t RefCount;
};

Expand All @@ -32,11 +33,19 @@ struct FFixedUObjectArray {
/// TEST
struct FChunkedFixedUObjectArray {
(FUObjectItem*)* Objects; // Array of pointers to FUObjectItem chunks
FUObjectItem* PreAllocatedObjects;
int32_t MaxElements;
int32_t NumElements;
int32_t MaxChunks;
int32_t NumChunks;
if (UE_VERSION >= 508) {
int32_t NumElements;
int32_t MaxElements;
int32_t NumChunks;
int32_t MaxChunks;
FUObjectItem* PreAllocatedObjects;
} else {
FUObjectItem* PreAllocatedObjects;
int32_t MaxElements;
int32_t NumElements;
int32_t MaxChunks;
int32_t NumChunks;
}
};

template<int Size>
Expand Down Expand Up @@ -79,6 +88,8 @@ class FUObjectDeleteListener {
/// FUObjectArray core structure
/// TEST
struct FUObjectArray {
if (UE_VERSION >= 508) FChunkedFixedUObjectArray ObjObjects;

int32_t ObjFirstGCIndex;
int32_t ObjLastNonGCIndex;
if (UE_VERSION >= 411) int32_t MaxObjectsNotConsideredByGC;
Expand All @@ -88,17 +99,21 @@ struct FUObjectArray {
if (UE_VERSION == 407) FUObjectArrayOlder ObjObjects;
else if (UE_VERSION < 411) FUObjectArrayOld ObjObjects;
else if (UE_VERSION < 420) FFixedUObjectArray ObjObjects;
else FChunkedFixedUObjectArray ObjObjects;
else if (UE_VERSION < 508) FChunkedFixedUObjectArray ObjObjects;

if (UE_VERSION == 407) TArray<int> ObjAvailable;
else {
if (UE_VERSION >= 408) FCriticalSection ObjObjectsCritical;
if (UE_VERSION < 422) TLockFreePointerListUnordered<int, 128> ObjAvailableList;
else if (UE_VERSION < 427) TLockFreePointerListUnordered<int, 64> ObjAvailableList;
else TArray<int> ObjAvailableList;

if (UE_VERSION >= 507) int32_t ObjAvailableListEstimateCount;
}

TArray<FUObjectCreateListener*> UObjectCreateListeners;
if (UE_VERSION >= 508) FCriticalSection UObjectCreateListenersCritical;

TArray<FUObjectDeleteListener*> UObjectDeleteListeners;

if (UE_VERSION >= 409) FCriticalSection UObjectDeleteListenersCritical;
Expand Down
Loading