mirror of
https://github.com/vosen/ZLUDA.git
synced 2025-04-12 10:48:53 +03:00
Move zluda_dump to the new CUDA infrastructure
This commit is contained in:
@ -1,6 +1,8 @@
|
||||
[workspace]
|
||||
|
||||
members = [
|
||||
"cuda_base",
|
||||
"cuda_types",
|
||||
"detours-sys",
|
||||
"hip_runtime-sys",
|
||||
"level_zero-sys",
|
||||
|
14
cuda_base/Cargo.toml
Normal file
14
cuda_base/Cargo.toml
Normal file
@ -0,0 +1,14 @@
|
||||
[package]
|
||||
name = "cuda_base"
|
||||
version = "0.0.0"
|
||||
authors = ["Andrzej Janik <vosen@vosen.pl>"]
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
quote = "1.0"
|
||||
syn = { version = "1.0", features = ["full", "visit-mut"] }
|
||||
proc-macro2 = "1.0"
|
||||
rustc-hash = "1.1.0"
|
||||
|
||||
[lib]
|
||||
proc-macro = true
|
1
cuda_base/README
Normal file
1
cuda_base/README
Normal file
@ -0,0 +1 @@
|
||||
bindgen /usr/local/cuda/include/cuda.h -o src/cuda.rs --with-derive-eq --whitelist-function="^cu.*" --whitelist-var="^CU.*" --size_t-is-usize --default-enum-style=newtype --no-layout-tests --no-doc-comments --no-derive-debug --new-type-alias "^CUdevice_v\d+$|^CUdeviceptr_v\d+$" --must-use-type "cudaError_enum" -- -D__CUDA_API_VERSION_INTERNAL
|
File diff suppressed because it is too large
Load Diff
485
cuda_base/src/lib.rs
Normal file
485
cuda_base/src/lib.rs
Normal file
@ -0,0 +1,485 @@
|
||||
extern crate proc_macro;
|
||||
|
||||
use std::collections::hash_map;
|
||||
use std::iter;
|
||||
|
||||
use proc_macro::TokenStream;
|
||||
use proc_macro2::Span;
|
||||
use quote::{format_ident, quote, ToTokens};
|
||||
use rustc_hash::{FxHashMap, FxHashSet};
|
||||
use syn::parse::{Parse, ParseStream};
|
||||
use syn::punctuated::Punctuated;
|
||||
use syn::token::Brace;
|
||||
use syn::visit_mut::VisitMut;
|
||||
use syn::{
|
||||
bracketed, parse_macro_input, Abi, Fields, File, FnArg, ForeignItem, ForeignItemFn, Ident,
|
||||
Item, ItemForeignMod, ItemMacro, LitStr, Macro, MacroDelimiter, PatType, Path, PathArguments,
|
||||
PathSegment, ReturnType, Signature, Token, Type, TypeArray, TypePath, TypePtr,
|
||||
};
|
||||
|
||||
const CUDA_RS: &'static str = include_str! {"cuda.rs"};
|
||||
|
||||
// This macro copies cuda.rs as-is with some changes:
|
||||
// * All function declarations are filtered out
|
||||
// * CUdeviceptr_v2 is redefined from `unsigned long long` to `*void`
|
||||
// * `extern "C"` gets replaced by `extern "system"`
|
||||
// * CUuuid_st is redefined to use uchar instead of char
|
||||
#[proc_macro]
|
||||
pub fn cuda_type_declarations(_: TokenStream) -> TokenStream {
|
||||
let mut cuda_module = syn::parse_str::<File>(CUDA_RS).unwrap();
|
||||
cuda_module.items = cuda_module
|
||||
.items
|
||||
.into_iter()
|
||||
.filter_map(|item| match item {
|
||||
Item::ForeignMod(_) => None,
|
||||
Item::Struct(mut struct_) => {
|
||||
if "CUdeviceptr_v2" == struct_.ident.to_string() {
|
||||
match &mut struct_.fields {
|
||||
Fields::Unnamed(ref mut fields) => {
|
||||
fields.unnamed[0].ty =
|
||||
absolute_path_to_mut_ptr(&["std", "os", "raw", "c_void"])
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
} else if "CUuuid_st" == struct_.ident.to_string() {
|
||||
match &mut struct_.fields {
|
||||
Fields::Named(ref mut fields) => match fields.named[0].ty {
|
||||
Type::Array(TypeArray { ref mut elem, .. }) => {
|
||||
*elem = Box::new(Type::Path(TypePath {
|
||||
qself: None,
|
||||
path: segments_to_path(&["std", "os", "raw", "c_uchar"]),
|
||||
}))
|
||||
}
|
||||
_ => unreachable!(),
|
||||
},
|
||||
_ => panic!(),
|
||||
}
|
||||
}
|
||||
Some(Item::Struct(struct_))
|
||||
}
|
||||
i => Some(i),
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
syn::visit_mut::visit_file_mut(&mut FixAbi, &mut cuda_module);
|
||||
cuda_module.into_token_stream().into()
|
||||
}
|
||||
|
||||
fn segments_to_path(path: &[&'static str]) -> Path {
|
||||
let mut segments = Punctuated::new();
|
||||
for ident in path {
|
||||
let ident = PathSegment {
|
||||
ident: Ident::new(ident, Span::call_site()),
|
||||
arguments: PathArguments::None,
|
||||
};
|
||||
segments.push(ident);
|
||||
}
|
||||
Path {
|
||||
leading_colon: Some(Token)),
|
||||
segments,
|
||||
}
|
||||
}
|
||||
|
||||
fn absolute_path_to_mut_ptr(path: &[&'static str]) -> Type {
|
||||
Type::Ptr(TypePtr {
|
||||
star_token: Token),
|
||||
const_token: None,
|
||||
mutability: Some(Token)),
|
||||
elem: Box::new(Type::Path(TypePath {
|
||||
qself: None,
|
||||
path: segments_to_path(path),
|
||||
})),
|
||||
})
|
||||
}
|
||||
|
||||
struct FixAbi;
|
||||
|
||||
impl VisitMut for FixAbi {
|
||||
fn visit_abi_mut(&mut self, i: &mut Abi) {
|
||||
if let Some(ref mut name) = i.name {
|
||||
*name = LitStr::new("system", Span::call_site());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This macro accepts following arguments:
|
||||
// * `type_path`: path to the module with type definitions (in the module tree)
|
||||
// * `normal_macro`: ident for a normal macro
|
||||
// * `override_macro`: ident for an override macro
|
||||
// * `override_fns`: list of override functions
|
||||
// Then macro goes through every function in rust.rs, and for every fn `foo`:
|
||||
// * if `foo` is contained in `override_fns` then pass it into `override_macro`
|
||||
// * if `foo` is not contained in `override_fns` pass it to `normal_macro`
|
||||
// Both `override_macro` and `normal_macro` expect this format:
|
||||
// macro_foo!("system" fn cuCtxDetach(ctx: CUcontext) -> CUresult)
|
||||
// Additionally, it does a fixup of CUDA types so they get prefixed with `type_path`
|
||||
#[proc_macro]
|
||||
pub fn cuda_function_declarations(tokens: TokenStream) -> TokenStream {
|
||||
let input = parse_macro_input!(tokens as FnDeclInput);
|
||||
let cuda_module = syn::parse_str::<File>(CUDA_RS).unwrap();
|
||||
let override_fns = input
|
||||
.override_fns
|
||||
.iter()
|
||||
.map(ToString::to_string)
|
||||
.collect::<FxHashSet<_>>();
|
||||
cuda_module
|
||||
.items
|
||||
.into_iter()
|
||||
.filter_map(|item| match item {
|
||||
Item::ForeignMod(ItemForeignMod { mut items, .. }) => match items.pop().unwrap() {
|
||||
ForeignItem::Fn(ForeignItemFn {
|
||||
sig:
|
||||
Signature {
|
||||
ident,
|
||||
inputs,
|
||||
output,
|
||||
..
|
||||
},
|
||||
..
|
||||
}) => {
|
||||
let path = if override_fns.contains(&ident.to_string()) {
|
||||
&input.override_macro
|
||||
} else {
|
||||
&input.normal_macro
|
||||
}
|
||||
.clone();
|
||||
let inputs = inputs
|
||||
.into_iter()
|
||||
.map(|fn_arg| match fn_arg {
|
||||
FnArg::Typed(mut pat_type) => {
|
||||
pat_type.ty =
|
||||
prepend_cuda_path_to_type(&input.type_path, pat_type.ty);
|
||||
FnArg::Typed(pat_type)
|
||||
}
|
||||
_ => unreachable!(),
|
||||
})
|
||||
.collect::<Punctuated<_, Token![,]>>();
|
||||
let output = match output {
|
||||
ReturnType::Type(_, type_) => type_,
|
||||
ReturnType::Default => unreachable!(),
|
||||
};
|
||||
let type_path = input.type_path.clone();
|
||||
let tokens = quote! {
|
||||
"system" fn #ident(#inputs) -> #type_path :: #output
|
||||
};
|
||||
Some(Item::Macro(ItemMacro {
|
||||
attrs: Vec::new(),
|
||||
ident: None,
|
||||
mac: Macro {
|
||||
path,
|
||||
bang_token: Token),
|
||||
delimiter: MacroDelimiter::Brace(Brace {
|
||||
span: Span::call_site(),
|
||||
}),
|
||||
tokens,
|
||||
},
|
||||
semi_token: None,
|
||||
}))
|
||||
}
|
||||
_ => unreachable!(),
|
||||
},
|
||||
_ => None,
|
||||
})
|
||||
.map(Item::into_token_stream)
|
||||
.collect::<proc_macro2::TokenStream>()
|
||||
.into()
|
||||
}
|
||||
|
||||
fn prepend_cuda_path_to_type(base_path: &Path, type_: Box<Type>) -> Box<Type> {
|
||||
match *type_ {
|
||||
Type::Path(mut type_path) => {
|
||||
type_path.path = prepend_cuda_path_to_path(base_path, type_path.path);
|
||||
Box::new(Type::Path(type_path))
|
||||
}
|
||||
Type::Ptr(mut type_ptr) => {
|
||||
type_ptr.elem = prepend_cuda_path_to_type(base_path, type_ptr.elem);
|
||||
Box::new(Type::Ptr(type_ptr))
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn prepend_cuda_path_to_path(base_path: &Path, path: Path) -> Path {
|
||||
if path.leading_colon.is_some() {
|
||||
return path;
|
||||
}
|
||||
if path.segments.len() == 1 {
|
||||
let ident = path.segments[0].ident.to_string();
|
||||
if ident.starts_with("CU") || ident.starts_with("cu") {
|
||||
let mut base_path = base_path.clone();
|
||||
base_path.segments.extend(path.segments);
|
||||
return base_path;
|
||||
}
|
||||
}
|
||||
path
|
||||
}
|
||||
|
||||
struct FnDeclInput {
|
||||
type_path: Path,
|
||||
normal_macro: Path,
|
||||
override_macro: Path,
|
||||
override_fns: Punctuated<Ident, Token![,]>,
|
||||
}
|
||||
|
||||
impl Parse for FnDeclInput {
|
||||
fn parse(input: ParseStream) -> syn::Result<Self> {
|
||||
let type_path = input.parse::<Path>()?;
|
||||
input.parse::<Token![,]>()?;
|
||||
let normal_macro = input.parse::<Path>()?;
|
||||
input.parse::<Token![,]>()?;
|
||||
let override_macro = input.parse::<Path>()?;
|
||||
input.parse::<Token![,]>()?;
|
||||
let override_fns_content;
|
||||
bracketed!(override_fns_content in input);
|
||||
let override_fns = override_fns_content.parse_terminated(Ident::parse)?;
|
||||
Ok(Self {
|
||||
type_path,
|
||||
normal_macro,
|
||||
override_macro,
|
||||
override_fns,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// This trait accepts following parameters:
|
||||
// * `type_path`: path to the module with type definitions (in the module tree)
|
||||
// * `trait_`: name of the trait to be derived
|
||||
// * `ignore_structs`: bracketed list of types to ignore
|
||||
// * `ignore_fns`: bracketed list of fns to ignore
|
||||
#[proc_macro]
|
||||
pub fn cuda_derive_display_trait(tokens: TokenStream) -> TokenStream {
|
||||
let input = parse_macro_input!(tokens as DeriveDisplayInput);
|
||||
let cuda_module = syn::parse_str::<File>(CUDA_RS).unwrap();
|
||||
let mut derive_state = DeriveDisplayState::new(input);
|
||||
cuda_module
|
||||
.items
|
||||
.into_iter()
|
||||
.filter_map(|i| cuda_derive_display_trait_for_item(&mut derive_state, i))
|
||||
.collect::<proc_macro2::TokenStream>()
|
||||
.into()
|
||||
}
|
||||
|
||||
fn cuda_derive_display_trait_for_item(
|
||||
state: &mut DeriveDisplayState,
|
||||
item: Item,
|
||||
) -> Option<proc_macro2::TokenStream> {
|
||||
let path_prefix = &state.type_path;
|
||||
let path_prefix_iter = iter::repeat(&path_prefix);
|
||||
let trait_ = &state.trait_;
|
||||
let trait_iter = iter::repeat(&state.trait_);
|
||||
match item {
|
||||
Item::Const(_) => None,
|
||||
Item::ForeignMod(ItemForeignMod { mut items, .. }) => match items.pop().unwrap() {
|
||||
ForeignItem::Fn(ForeignItemFn {
|
||||
sig: Signature { ident, inputs, .. },
|
||||
..
|
||||
}) => {
|
||||
if state.ignore_fns.contains(&ident) {
|
||||
return None;
|
||||
}
|
||||
let inputs = inputs
|
||||
.into_iter()
|
||||
.map(|fn_arg| match fn_arg {
|
||||
FnArg::Typed(mut pat_type) => {
|
||||
pat_type.ty = prepend_cuda_path_to_type(path_prefix, pat_type.ty);
|
||||
FnArg::Typed(pat_type)
|
||||
}
|
||||
_ => unreachable!(),
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
let inputs_iter = inputs.iter();
|
||||
let mut arg_name_iter = inputs.iter().map(|fn_arg| match fn_arg {
|
||||
FnArg::Typed(PatType { pat, .. }) => pat,
|
||||
_ => unreachable!(),
|
||||
});
|
||||
let fn_name = format_ident!("write_{}", ident);
|
||||
Some(match arg_name_iter.next() {
|
||||
Some(first_arg_name) => quote! {
|
||||
pub fn #fn_name(writer: &mut (impl std::io::Write + ?Sized), #(#inputs_iter,)*) -> std::io::Result<()> {
|
||||
writer.write_all(concat!("(", stringify!(#first_arg_name), ": ").as_bytes())?;
|
||||
CudaDisplay::write(&#first_arg_name, writer)?;
|
||||
#(
|
||||
writer.write_all(b", ")?;
|
||||
writer.write_all(concat!(stringify!(#arg_name_iter), ": ").as_bytes())?;
|
||||
CudaDisplay::write(&#arg_name_iter, writer)?;
|
||||
)*
|
||||
writer.write_all(b")")
|
||||
}
|
||||
},
|
||||
None => quote! {
|
||||
pub fn #fn_name(writer: &mut (impl std::io::Write + ?Sized)) -> std::io::Result<()> {
|
||||
writer.write_all(b"()")
|
||||
}
|
||||
},
|
||||
})
|
||||
}
|
||||
_ => unreachable!(),
|
||||
},
|
||||
Item::Impl(mut item_impl) => {
|
||||
let enum_ = match *(item_impl.self_ty) {
|
||||
Type::Path(mut path) => path.path.segments.pop().unwrap().into_value().ident,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
let variant_ = match item_impl.items.pop().unwrap() {
|
||||
syn::ImplItem::Const(item_const) => item_const.ident,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
state.record_enum_variant(enum_, variant_);
|
||||
None
|
||||
}
|
||||
Item::Struct(item_struct) => {
|
||||
let item_struct_name = item_struct.ident.to_string();
|
||||
if state.ignore_structs.contains(&item_struct.ident) {
|
||||
return None;
|
||||
}
|
||||
if item_struct_name.ends_with("_enum") {
|
||||
let enum_ = &item_struct.ident;
|
||||
let enum_iter = iter::repeat(&item_struct.ident);
|
||||
let variants = state.enums.get(&item_struct.ident).unwrap().iter();
|
||||
Some(quote! {
|
||||
impl #trait_ for #path_prefix :: #enum_ {
|
||||
fn write(&self, writer: &mut (impl std::io::Write + ?Sized)) -> std::io::Result<()> {
|
||||
match self {
|
||||
#(& #path_prefix_iter :: #enum_iter :: #variants => writer.write_all(stringify!(#variants).as_bytes()),)*
|
||||
_ => write!(writer, "{}", self.0)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
} else {
|
||||
let struct_ = &item_struct.ident;
|
||||
let (first_field, rest_of_fields) = match item_struct.fields {
|
||||
Fields::Named(fields) => {
|
||||
let mut all_idents = fields.named.into_iter().filter_map(|f| {
|
||||
let f_ident = f.ident.unwrap();
|
||||
let name = f_ident.to_string();
|
||||
if name.starts_with("reserved") || name == "_unused" {
|
||||
None
|
||||
} else {
|
||||
Some(f_ident)
|
||||
}
|
||||
});
|
||||
let first = match all_idents.next() {
|
||||
Some(f) => f,
|
||||
None => return None,
|
||||
};
|
||||
(first, all_idents)
|
||||
}
|
||||
_ => return None,
|
||||
};
|
||||
Some(quote! {
|
||||
impl #trait_ for #path_prefix :: #struct_ {
|
||||
fn write(&self, writer: &mut (impl std::io::Write + ?Sized)) -> std::io::Result<()> {
|
||||
writer.write_all(concat!("{ ", stringify!(#first_field), ": ").as_bytes())?;
|
||||
#trait_::write(&self.#first_field, writer)?;
|
||||
#(
|
||||
writer.write_all(concat!(", ", stringify!(#rest_of_fields), ": ").as_bytes())?;
|
||||
#trait_iter::write(&self.#rest_of_fields, writer)?;
|
||||
)*
|
||||
writer.write_all(b" }")
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Item::Type(item_type) => match *(item_type.ty) {
|
||||
Type::Ptr(_) => {
|
||||
let type_ = item_type.ident;
|
||||
Some(quote! {
|
||||
impl #trait_ for #path_prefix :: #type_ {
|
||||
fn write(&self, writer: &mut (impl std::io::Write + ?Sized)) -> std::io::Result<()> {
|
||||
write!(writer, "{:p}", *self)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
Type::Path(type_path) => {
|
||||
if type_path.path.leading_colon.is_some() {
|
||||
let option_seg = type_path.path.segments.last().unwrap();
|
||||
if option_seg.ident == "Option" {
|
||||
match &option_seg.arguments {
|
||||
PathArguments::AngleBracketed(generic) => match generic.args[0] {
|
||||
syn::GenericArgument::Type(Type::BareFn(_)) => {
|
||||
let type_ = &item_type.ident;
|
||||
return Some(quote! {
|
||||
impl #trait_ for #path_prefix :: #type_ {
|
||||
fn write(&self, writer: &mut (impl std::io::Write + ?Sized)) -> std::io::Result<()> {
|
||||
write!(writer, "{:p}", unsafe { std::mem::transmute::<#path_prefix :: #type_, *mut ::std::ffi::c_void>(*self) })
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
_ => unreachable!(),
|
||||
},
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
_ => unreachable!(),
|
||||
},
|
||||
Item::Union(_) => None,
|
||||
Item::Use(_) => None,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
struct DeriveDisplayState {
|
||||
type_path: Path,
|
||||
trait_: Path,
|
||||
ignore_structs: FxHashSet<Ident>,
|
||||
ignore_fns: FxHashSet<Ident>,
|
||||
enums: FxHashMap<Ident, Vec<Ident>>,
|
||||
}
|
||||
|
||||
impl DeriveDisplayState {
|
||||
fn new(input: DeriveDisplayInput) -> Self {
|
||||
DeriveDisplayState {
|
||||
type_path: input.type_path,
|
||||
trait_: input.trait_,
|
||||
ignore_structs: input.ignore_structs.into_iter().collect(),
|
||||
ignore_fns: input.ignore_fns.into_iter().collect(),
|
||||
enums: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
fn record_enum_variant(&mut self, enum_: Ident, variant: Ident) {
|
||||
match self.enums.entry(enum_) {
|
||||
hash_map::Entry::Occupied(mut entry) => {
|
||||
entry.get_mut().push(variant);
|
||||
}
|
||||
hash_map::Entry::Vacant(entry) => {
|
||||
entry.insert(vec![variant]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct DeriveDisplayInput {
|
||||
type_path: Path,
|
||||
trait_: Path,
|
||||
ignore_structs: Punctuated<Ident, Token![,]>,
|
||||
ignore_fns: Punctuated<Ident, Token![,]>,
|
||||
}
|
||||
|
||||
impl Parse for DeriveDisplayInput {
|
||||
fn parse(input: ParseStream) -> syn::Result<Self> {
|
||||
let type_path = input.parse::<Path>()?;
|
||||
input.parse::<Token![,]>()?;
|
||||
let trait_ = input.parse::<Path>()?;
|
||||
input.parse::<Token![,]>()?;
|
||||
let ignore_structs_buffer;
|
||||
bracketed!(ignore_structs_buffer in input);
|
||||
let ignore_structs = ignore_structs_buffer.parse_terminated(Ident::parse)?;
|
||||
input.parse::<Token![,]>()?;
|
||||
let ignore_fns_buffer;
|
||||
bracketed!(ignore_fns_buffer in input);
|
||||
let ignore_fns = ignore_fns_buffer.parse_terminated(Ident::parse)?;
|
||||
Ok(Self {
|
||||
type_path,
|
||||
trait_,
|
||||
ignore_structs,
|
||||
ignore_fns,
|
||||
})
|
||||
}
|
||||
}
|
8
cuda_types/Cargo.toml
Normal file
8
cuda_types/Cargo.toml
Normal file
@ -0,0 +1,8 @@
|
||||
[package]
|
||||
name = "cuda_types"
|
||||
version = "0.0.0"
|
||||
authors = ["Andrzej Janik <vosen@vosen.pl>"]
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
cuda_base = { path = "../cuda_base" }
|
3
cuda_types/src/lib.rs
Normal file
3
cuda_types/src/lib.rs
Normal file
@ -0,0 +1,3 @@
|
||||
use cuda_base::cuda_type_declarations;
|
||||
|
||||
cuda_type_declarations!();
|
@ -18,6 +18,8 @@ lazy_static = "1.4"
|
||||
# we don't need elf32, but goblin has a bug where elf64 does not build without elf32
|
||||
goblin = { version = "0.4", default-features = false, features = ["elf64", "elf32", "archive"] }
|
||||
paste = "1.0"
|
||||
cuda_base = { path = "../cuda_base" }
|
||||
cuda_types = { path = "../cuda_types" }
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
winapi = { version = "0.3", features = ["libloaderapi", "debugapi", "std"] }
|
||||
|
@ -1,11 +1,9 @@
|
||||
use crate::format;
|
||||
use crate::{log, os, trace::StateTracker};
|
||||
use crate::{log::UInt, GlobalDelayedState};
|
||||
use cuda_types::{CUmodule, CUresult, CUuuid};
|
||||
use std::borrow::Cow;
|
||||
|
||||
use crate::{
|
||||
cuda::{CUmodule, CUresult, CUuuid},
|
||||
log, os,
|
||||
trace::StateTracker,
|
||||
};
|
||||
use std::hash::Hash;
|
||||
use std::{
|
||||
collections::{hash_map, HashMap},
|
||||
ffi::c_void,
|
||||
@ -17,10 +15,19 @@ use std::{
|
||||
pub(crate) struct DarkApiState {
|
||||
// Key is Box<CUuuid, because thunk reporting unknown export table needs a
|
||||
// stablememory location for the guid
|
||||
overrides: HashMap<Box<CUuuid>, Vec<*const c_void>>,
|
||||
overrides: HashMap<Box<CUuuidWrapper>, Vec<*const c_void>>,
|
||||
original: OriginalExports,
|
||||
}
|
||||
|
||||
#[derive(Eq, PartialEq)]
|
||||
pub(crate) struct CUuuidWrapper(pub CUuuid);
|
||||
|
||||
impl Hash for CUuuidWrapper {
|
||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||
self.0.bytes.hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct OriginalExports {
|
||||
original_get_module_from_cubin: Option<
|
||||
unsafe extern "system" fn(
|
||||
@ -69,11 +76,13 @@ pub(crate) fn override_export_table(
|
||||
) {
|
||||
let state = &mut state.dark_api;
|
||||
let export_table_mut = unsafe { &mut *pp_export_table };
|
||||
let export_id = Box::new(unsafe { *p_export_table_id });
|
||||
let export_id = Box::new(CUuuidWrapper(unsafe { *p_export_table_id }));
|
||||
*export_table_mut = match state.overrides.entry(export_id) {
|
||||
hash_map::Entry::Occupied(entry) => entry.get().as_ptr() as *const _,
|
||||
hash_map::Entry::Vacant(entry) => {
|
||||
let guid_ptr = &**entry.key() as *const _;
|
||||
let guid_ptr = unsafe {
|
||||
mem::transmute::<*const CUuuidWrapper, *const CUuuid>(&**entry.key() as *const _)
|
||||
};
|
||||
entry
|
||||
.insert(unsafe {
|
||||
create_new_override(*pp_export_table as *const _, guid_ptr, &mut state.original)
|
||||
@ -123,7 +132,7 @@ unsafe extern "system" fn report_unknown_export_table_call(
|
||||
if let Ok(mut global_state) = crate::GLOBAL_STATE.lock() {
|
||||
let mut logger = global_state
|
||||
.log_factory
|
||||
.get_logger_dark_api(*export_table, idx);
|
||||
.get_logger_dark_api(*export_table, idx, None);
|
||||
logger.log(log::LogEntry::UnknownExportTableFn)
|
||||
}
|
||||
}
|
||||
@ -351,10 +360,21 @@ unsafe extern "system" fn get_module_from_cubin(
|
||||
module: *mut CUmodule,
|
||||
fatbinc_wrapper: *const FatbincWrapper,
|
||||
) -> CUresult {
|
||||
let arguments_writer = Box::new(move |writer: &mut dyn std::io::Write| {
|
||||
writer.write_all(b"(")?;
|
||||
writer.write_all(stringify!(module).as_bytes())?;
|
||||
writer.write_all(b": ")?;
|
||||
format::CudaDisplay::write(&module, writer)?;
|
||||
writer.write_all(b", ")?;
|
||||
writer.write_all(stringify!(fatbinc_wrapper).as_bytes())?;
|
||||
write!(writer, ": {:p})", fatbinc_wrapper)
|
||||
});
|
||||
let global_state = &mut *super::GLOBAL_STATE.lock().unwrap();
|
||||
let mut fn_logger = global_state
|
||||
.log_factory
|
||||
.get_logger_dark_api(CUDART_INTERFACE_GUID, 1);
|
||||
let mut fn_logger = global_state.log_factory.get_logger_dark_api(
|
||||
CUDART_INTERFACE_GUID,
|
||||
1,
|
||||
Some(arguments_writer),
|
||||
);
|
||||
let global_state = &mut *super::GLOBAL_STATE.lock().unwrap();
|
||||
let delayed_state = global_state.delayed_state.unwrap_mut();
|
||||
record_submodules_from_wrapped_fatbin(
|
||||
@ -375,10 +395,27 @@ unsafe extern "system" fn get_module_from_cubin_ext1(
|
||||
ptr2: *mut c_void,
|
||||
_unknown: usize,
|
||||
) -> CUresult {
|
||||
let arguments_writer = Box::new(move |writer: &mut dyn std::io::Write| {
|
||||
writer.write_all(b"(")?;
|
||||
writer.write_all(stringify!(module).as_bytes())?;
|
||||
writer.write_all(b": ")?;
|
||||
format::CudaDisplay::write(&module, writer)?;
|
||||
writer.write_all(b", ")?;
|
||||
writer.write_all(stringify!(fatbinc_wrapper).as_bytes())?;
|
||||
write!(writer, ": {:p}, ", fatbinc_wrapper)?;
|
||||
writer.write_all(stringify!(ptr1).as_bytes())?;
|
||||
write!(writer, ": {:p}, ", ptr1)?;
|
||||
writer.write_all(stringify!(ptr2).as_bytes())?;
|
||||
write!(writer, ": {:p}, ", ptr2)?;
|
||||
writer.write_all(stringify!(_unknown).as_bytes())?;
|
||||
write!(writer, ": {})", _unknown)
|
||||
});
|
||||
let global_state = &mut *super::GLOBAL_STATE.lock().unwrap();
|
||||
let mut fn_logger = global_state
|
||||
.log_factory
|
||||
.get_logger_dark_api(CUDART_INTERFACE_GUID, 6);
|
||||
let mut fn_logger = global_state.log_factory.get_logger_dark_api(
|
||||
CUDART_INTERFACE_GUID,
|
||||
6,
|
||||
Some(arguments_writer),
|
||||
);
|
||||
if ptr1 != ptr::null_mut() {
|
||||
fn_logger.log(log::LogEntry::UnexpectedArgument {
|
||||
arg_name: stringify!(ptr1),
|
||||
@ -421,10 +458,27 @@ unsafe extern "system" fn get_module_from_cubin_ext2(
|
||||
ptr2: *mut c_void,
|
||||
_unknown: usize,
|
||||
) -> CUresult {
|
||||
let arguments_writer = Box::new(move |writer: &mut dyn std::io::Write| {
|
||||
writer.write_all(b"(")?;
|
||||
writer.write_all(stringify!(fatbin_header).as_bytes())?;
|
||||
write!(writer, ": {:p}, ", fatbin_header)?;
|
||||
writer.write_all(stringify!(module).as_bytes())?;
|
||||
writer.write_all(b": ")?;
|
||||
format::CudaDisplay::write(&module, writer)?;
|
||||
writer.write_all(b", ")?;
|
||||
writer.write_all(stringify!(ptr1).as_bytes())?;
|
||||
write!(writer, ": {:p}, ", ptr1)?;
|
||||
writer.write_all(stringify!(ptr2).as_bytes())?;
|
||||
write!(writer, ": {:p}, ", ptr2)?;
|
||||
writer.write_all(stringify!(_unknown).as_bytes())?;
|
||||
write!(writer, ": {})", _unknown)
|
||||
});
|
||||
let global_state = &mut *super::GLOBAL_STATE.lock().unwrap();
|
||||
let mut fn_logger = global_state
|
||||
.log_factory
|
||||
.get_logger_dark_api(CUDART_INTERFACE_GUID, 8);
|
||||
let mut fn_logger = global_state.log_factory.get_logger_dark_api(
|
||||
CUDART_INTERFACE_GUID,
|
||||
8,
|
||||
Some(arguments_writer),
|
||||
);
|
||||
if ptr1 != ptr::null_mut() {
|
||||
fn_logger.log(log::LogEntry::UnexpectedArgument {
|
||||
arg_name: stringify!(ptr1),
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,14 +1,11 @@
|
||||
use crate::cuda::CUmodule;
|
||||
use crate::cuda::CUuuid;
|
||||
use crate::format;
|
||||
use crate::format::FormatCudaObject;
|
||||
use cuda_types::CUmodule;
|
||||
use cuda_types::CUuuid;
|
||||
|
||||
use super::CUresult;
|
||||
use super::Settings;
|
||||
use std::borrow::Cow;
|
||||
use std::error::Error;
|
||||
use std::ffi::c_void;
|
||||
use std::ffi::FromBytesWithNulError;
|
||||
use std::ffi::NulError;
|
||||
use std::fmt::Display;
|
||||
use std::fs::File;
|
||||
@ -185,8 +182,9 @@ impl Factory {
|
||||
pub(crate) fn get_first_logger_and_init_settings(
|
||||
&mut self,
|
||||
func: &'static str,
|
||||
arguments_writer: Box<dyn FnMut(&mut dyn std::io::Write) -> std::io::Result<()>>,
|
||||
) -> (FunctionLogger, Settings) {
|
||||
let mut first_logger = self.get_logger(func);
|
||||
let mut first_logger = self.get_logger(func, arguments_writer);
|
||||
let settings = Settings::read_and_init(&mut first_logger);
|
||||
match Self::initalize_fallible_emitter(&settings) {
|
||||
Ok(fallible_emitter) => {
|
||||
@ -201,7 +199,11 @@ impl Factory {
|
||||
(first_logger, settings)
|
||||
}
|
||||
|
||||
pub(crate) fn get_logger(&mut self, func: &'static str) -> FunctionLogger {
|
||||
pub(crate) fn get_logger(
|
||||
&mut self,
|
||||
func: &'static str,
|
||||
arguments_writer: Box<dyn FnMut(&mut dyn std::io::Write) -> std::io::Result<()>>,
|
||||
) -> FunctionLogger {
|
||||
FunctionLogger {
|
||||
result: None,
|
||||
name: CudaFunctionName::Normal(func),
|
||||
@ -209,12 +211,16 @@ impl Factory {
|
||||
infallible_emitter: &mut self.infallible_emitter,
|
||||
write_buffer: &mut self.write_buffer,
|
||||
log_queue: &mut self.log_queue,
|
||||
finished_writing_args: false,
|
||||
args_to_write: 0,
|
||||
arguments_writer: Some(arguments_writer),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn get_logger_dark_api(&mut self, guid: CUuuid, index: usize) -> FunctionLogger {
|
||||
pub(crate) fn get_logger_dark_api(
|
||||
&mut self,
|
||||
guid: CUuuid,
|
||||
index: usize,
|
||||
arguments_writer: Option<Box<dyn FnMut(&mut dyn std::io::Write) -> std::io::Result<()>>>,
|
||||
) -> FunctionLogger {
|
||||
FunctionLogger {
|
||||
result: None,
|
||||
name: CudaFunctionName::Dark { guid, index },
|
||||
@ -222,8 +228,7 @@ impl Factory {
|
||||
infallible_emitter: &mut self.infallible_emitter,
|
||||
write_buffer: &mut self.write_buffer,
|
||||
log_queue: &mut self.log_queue,
|
||||
finished_writing_args: false,
|
||||
args_to_write: 0,
|
||||
arguments_writer,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -243,10 +248,9 @@ pub(crate) struct FunctionLogger<'a> {
|
||||
name: CudaFunctionName,
|
||||
infallible_emitter: &'a mut Box<dyn WriteTrailingZeroAware>,
|
||||
fallible_emitter: &'a mut Option<Box<dyn WriteTrailingZeroAware>>,
|
||||
arguments_writer: Option<Box<dyn FnMut(&mut dyn std::io::Write) -> std::io::Result<()>>>,
|
||||
write_buffer: &'a mut WriteBuffer,
|
||||
log_queue: &'a mut Vec<LogEntry>,
|
||||
args_to_write: usize,
|
||||
finished_writing_args: bool,
|
||||
}
|
||||
|
||||
impl<'a> FunctionLogger<'a> {
|
||||
@ -261,22 +265,31 @@ impl<'a> FunctionLogger<'a> {
|
||||
}
|
||||
|
||||
fn flush_log_queue_to_write_buffer(&mut self) {
|
||||
// TODO: remove this once everything has been converted to dtailed logging
|
||||
if !self.finished_writing_args {
|
||||
self.begin_writing_arguments(0);
|
||||
self.write_buffer.write("...) -> ");
|
||||
}
|
||||
if let Some(result) = self.result {
|
||||
match format::stringify_CUresult(result) {
|
||||
Some(text) => self.write_buffer.write(text),
|
||||
None => write!(self.write_buffer, "{}", result.0).unwrap(),
|
||||
self.write_buffer.start_line();
|
||||
match self.name {
|
||||
CudaFunctionName::Normal(fn_name) => self.write_buffer.write(fn_name),
|
||||
CudaFunctionName::Dark { guid, index } => {
|
||||
format::CudaDisplay::write(&guid, &mut self.write_buffer).ok();
|
||||
write!(&mut self.write_buffer, "::{}", index).ok();
|
||||
}
|
||||
}
|
||||
match &mut self.arguments_writer {
|
||||
Some(arg_writer) => {
|
||||
arg_writer(&mut self.write_buffer).ok();
|
||||
}
|
||||
None => {
|
||||
self.write_buffer.write_all(b"(...)").ok();
|
||||
}
|
||||
}
|
||||
self.write_buffer.write_all(b" -> ").ok();
|
||||
if let Some(result) = self.result {
|
||||
format::CudaDisplay::write(&result, self.write_buffer).ok();
|
||||
} else {
|
||||
self.write_buffer.write("(UNKNOWN)");
|
||||
self.write_buffer.write_all(b"UNKNOWN").ok();
|
||||
};
|
||||
self.write_buffer.end_line();
|
||||
for entry in self.log_queue.iter() {
|
||||
write!(self.write_buffer, " {}", entry).unwrap_or_else(|_| unreachable!());
|
||||
write!(self.write_buffer, " {}", entry).ok();
|
||||
self.write_buffer.end_line();
|
||||
}
|
||||
self.write_buffer.finish();
|
||||
@ -290,35 +303,6 @@ impl<'a> FunctionLogger<'a> {
|
||||
self.write_buffer.end_line();
|
||||
self.write_buffer.finish();
|
||||
}
|
||||
|
||||
pub(crate) fn begin_writing_arguments(&mut self, len: usize) {
|
||||
self.args_to_write = len;
|
||||
match self.name {
|
||||
CudaFunctionName::Normal(fn_name) => self.write_buffer.write(fn_name),
|
||||
CudaFunctionName::Dark { guid, index } => {
|
||||
guid.write_post_execution(CUresult::CUDA_SUCCESS, &mut self.write_buffer);
|
||||
write!(&mut self.write_buffer, "::{}", index).ok();
|
||||
}
|
||||
}
|
||||
self.write_buffer.write("(")
|
||||
}
|
||||
|
||||
pub(crate) fn write_single_argument<'x>(
|
||||
&mut self,
|
||||
result: CUresult,
|
||||
arg: impl FormatCudaObject,
|
||||
) {
|
||||
self.args_to_write -= 1;
|
||||
arg.write_post_execution(result, self.write_buffer);
|
||||
if self.args_to_write != 0 {
|
||||
self.write_buffer.write(", ")
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn end_writing_arguments(&mut self) {
|
||||
self.write_buffer.write(") -> ");
|
||||
self.finished_writing_args = true;
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Drop for FunctionLogger<'a> {
|
||||
@ -347,18 +331,12 @@ pub(crate) enum LogEntry {
|
||||
raw_image: *const c_void,
|
||||
kind: &'static str,
|
||||
},
|
||||
MalformedFunctionName(Utf8Error),
|
||||
FunctionParameter {
|
||||
name: &'static str,
|
||||
value: String,
|
||||
},
|
||||
MalformedModulePath(Utf8Error),
|
||||
NonUtf8ModuleText(Utf8Error),
|
||||
NulInsideModuleText(NulError),
|
||||
ModuleParsingError(String),
|
||||
Lz4DecompressionFailure,
|
||||
UnknownExportTableFn,
|
||||
UnknownModule(CUmodule),
|
||||
UnexpectedArgument {
|
||||
arg_name: &'static str,
|
||||
expected: Vec<UInt>,
|
||||
@ -406,7 +384,6 @@ impl Display for LogEntry {
|
||||
LogEntry::NulInsideModuleText(e) => e.fmt(f),
|
||||
LogEntry::Lz4DecompressionFailure => write!(f, "LZ4 decompression failure"),
|
||||
LogEntry::UnknownExportTableFn => write!(f, "Unknown export table function"),
|
||||
LogEntry::UnknownModule(hmod) => write!(f, "Unknown module {:?}", hmod),
|
||||
LogEntry::UnexpectedBinaryField {
|
||||
field_name,
|
||||
expected,
|
||||
@ -437,8 +414,6 @@ impl Display for LogEntry {
|
||||
.join(", "),
|
||||
observed
|
||||
),
|
||||
LogEntry::MalformedFunctionName(e) => e.fmt(f),
|
||||
LogEntry::FunctionParameter { name, value } => write!(f, "{}: {}", name, value),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,6 @@
|
||||
use std::{
|
||||
ffi::{c_void, CStr, CString, OsString},
|
||||
mem,
|
||||
os::raw::c_ushort,
|
||||
ptr,
|
||||
ffi::{c_void, CStr},
|
||||
mem, ptr,
|
||||
};
|
||||
|
||||
use std::os::windows::io::AsRawHandle;
|
||||
@ -12,7 +10,7 @@ use winapi::{
|
||||
um::libloaderapi::{GetProcAddress, LoadLibraryW},
|
||||
};
|
||||
|
||||
use crate::cuda::CUuuid;
|
||||
use cuda_types::CUuuid;
|
||||
|
||||
pub(crate) const LIBCUDA_DEFAULT_PATH: &'static str = "C:\\Windows\\System32\\nvcuda.dll";
|
||||
const LOAD_LIBRARY_NO_REDIRECT: &'static [u8] = b"ZludaLoadLibraryW_NoRedirect\0";
|
||||
|
@ -1,7 +1,8 @@
|
||||
use ptx::{ast::PtxError, Token};
|
||||
use ptx::{DisplayParseError, ModuleParserExt};
|
||||
|
||||
use crate::{cuda::CUmodule, dark_api, log, Settings};
|
||||
use crate::{dark_api, log, Settings};
|
||||
use cuda_types::CUmodule;
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
ffi::{c_void, CStr, CString},
|
||||
@ -171,7 +172,7 @@ impl StateTracker {
|
||||
submodule_index: Option<usize>,
|
||||
module_text: &str,
|
||||
) {
|
||||
let (ast, errors) = ptx::ModuleParser::parse_unchecked(module_text);
|
||||
let (_ast, errors) = ptx::ModuleParser::parse_unchecked(module_text);
|
||||
if !errors.is_empty() {
|
||||
fn_logger.log(log::LogEntry::ModuleParsingError(
|
||||
DumpWriter::get_file_name(module_index, version, submodule_index, "log"),
|
||||
@ -185,10 +186,6 @@ impl StateTracker {
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn module_exists(&self, hmod: CUmodule) -> bool {
|
||||
self.modules.contains_key(&hmod)
|
||||
}
|
||||
}
|
||||
|
||||
struct ParsedModule {
|
||||
|
Reference in New Issue
Block a user