Update rust example to use BPF enabled infrastructure (#2974)

This commit is contained in:
Jack May 2019-02-28 22:05:11 -08:00 committed by GitHub
parent 6b228df3df
commit b9524217fe
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 173 additions and 183 deletions

View File

@ -51,7 +51,9 @@ test-stable-perf)
_ make -C programs/bpf/c tests
# Must be built out of band
_ make -C programs/bpf/rust/noop/ all
_ pushd programs/bpf/rust/noop
./build.sh
popd
_ cargo test --manifest-path programs/Cargo.toml --no-default-features --features="$PROGRAM_FEATURES"
_ cargo test --manifest-path programs/native/bpf_loader/Cargo.toml --no-default-features --features="$PROGRAM_FEATURES"

View File

@ -1,4 +1,3 @@
/out/
/target/
Cargo.lock

View File

@ -1,5 +1,5 @@
# Note: This crate must be built using the makefile, try `make help` instead of `cargo build`
# Note: This crate must be built using build.sh
[package]
name = "solana-bpf-rust-noop"
@ -11,7 +11,15 @@ license = "Apache-2.0"
homepage = "https://solana.com/"
[dependencies]
heapless = { version = "0.4.0", default-features = false }
# byteorder = { version = "1.3.1", default-features = false }
# heapless = { version = "0.4.0", default-features = false }
# byte = { version = "0.2", default-features = false }
[workspace]
members = []
[lib]
name = "solana_bpf_rust_noop"
crate-type = ["cdylib"]

View File

@ -0,0 +1,4 @@
[dependencies.compiler_builtins]
path = "../../../../sdk/bpf/rust-bpf-sysroot/src/compiler-builtins"

25
programs/bpf/rust/noop/build.sh Executable file
View File

@ -0,0 +1,25 @@
#!/usr/bin/env bash
cargo install xargo
set -e
# Ensure the sdk is installed
../../../../sdk/bpf/scripts/install.sh
rustup override set bpf
export RUSTFLAGS="$RUSTFLAGS \
-C lto=no \
-C opt-level=2 \
-C link-arg=-Tbpf.ld \
-C link-arg=-z -C link-arg=notext \
-C link-arg=--Bdynamic \
-C link-arg=-shared \
-C link-arg=--entry=entrypoint \
-C linker=../../../../sdk/bpf/llvm-native/bin/ld.lld"
export XARGO_HOME="$PWD/target/xargo"
export XARGO_RUST_SRC="../../../../sdk/bpf/rust-bpf-sysroot/src"
# export XARGO_RUST_SRC="../../../../../rust-bpf-sysroot/src"
xargo build --target bpfel-unknown-unknown --release -v
{ { set +x; } 2>/dev/null; echo Success; }

View File

@ -0,0 +1,5 @@
#!/usr/bin/env bash
set -ex
cargo clean

12
programs/bpf/rust/noop/dump.sh Executable file
View File

@ -0,0 +1,12 @@
#!/usr/bin/env bash
cp dump.txt dump_last.txt 2>/dev/null
set -x
set -e
./clean.sh
./build.sh
ls -la ./target/bpfel_unknown_unknown/release/solana_bpf_rust_noop.so > dump.txt
greadelf -aW ./target/bpfel_unknown_unknown/release/solana_bpf_rust_noop.so | rustfilt >> dump.txt
llvm-objdump -print-imm-hex --source --disassemble ./target/bpfel_unknown_unknown/release/solana_bpf_rust_noop.so >> dump.txt

View File

@ -1,146 +0,0 @@
LOCAL_PATH := $(dir $(lastword $(MAKEFILE_LIST)))
SDK_PATH := $(abspath $(LOCAL_PATH)/../../../../sdk/bpf)
INSTALL_SH := $(abspath $(SDK_PATH)/scripts/install.sh)
all:
.PHONY: help all install dump clean
ifneq ($(V),1)
_@ :=@
endif
TARGET_NAME := solana_bpf_rust_noop
SRC_DIR ?= ./src
OUT_DIR ?= ./out
INSTALL_DIR ?= ./out
CARGO_OUT_DIR ?=$(LOCAL_PATH)target/release
ifeq ($(DOCKER),1)
$(warning DOCKER=1 is experimential and may not work as advertised)
LLVM_DIR = $(SDK_PATH)/llvm-docker
LLVM_SYSTEM_INC_DIRS := /usr/local/lib/clang/8.0.0/include
else
LLVM_DIR = $(SDK_PATH)/llvm-native
LLVM_SYSTEM_INC_DIRS := $(LLVM_DIR)/lib/clang/8.0.0/include
endif
CARGO := cargo
ifdef LLVM_DIR
LLC := $(LLVM_DIR)/bin/llc
LLD := $(LLVM_DIR)/bin/ld.lld
OBJ_COPY := $(LLVM_DIR)/bin/llvm-objcopy
OBJ_DUMP := $(LLVM_DIR)/bin/llvm-objdump
endif
CARGO_FLAGS := \
+nightly \
-vv rustc \
-vv \
--release \
-- \
--emit=llvm-ir \
-C panic=abort \
LLC_FLAGS := \
-march=bpf \
-filetype=obj \
LLD_FLAGS := \
-z notext \
-shared \
--Bdynamic \
$(LOCAL_PATH)bpf.ld \
--entry entrypoint \
OBJ_COPY_FLAGS := \
--remove-section .eh_frame \
OBJ_DUMP_FLAGS := \
-color \
-source \
-disassemble \
help:
@echo ''
@echo 'solana-bpf-rust-noop makefile'
@echo ''
@echo 'This makefile will build the solana-bpf-rust-noop crate into a BPF shared object'
@echo ''
@echo 'This makefile is not run as part of the Solana workspace. Doing so'
@echo 'would result in a cargo deadlock since this makefile also calls cargo with parameters'
@echo 'required to build the BPF program from Rust.'
@echo ''
@echo 'Note: Rust BPF programs are tested as part of the Solana integration tests when'
@echo ' feature "bpf_rust" is enabled. The solana-bpf-rust-noop crate must be built'
@echo ' with this makefile first before bulding Solana.'
@echo ''
@echo ' Here is a sample command that will run this BPF program:'
@echo ''
@echo ' export RUST_LOG=solana_bpf_loader=info; cargo test --features="bpf_rust" -- --nocapture test_program_bpf_rust'
@echo ''
@echo 'User settings'
@echo ' - The following setting are overridable on the command line, default values shown:'
@echo ' - Show commands while building: V=1'
@echo ' V=$(V)'
@echo ' - Location to place output files:'
@echo ' OUT_DIR=$(OUT_DIR)'
@echo ' - Location to install the final shared object:'
@echo ' INSTALL_DIR=$(INSTALL_DIR)'
@echo ' - Location of LLVM:'
@echo ' LLVM_DIR=$(LLVM_DIR)'
@echo ''
@echo 'Usage:'
@echo ' - make help - This help message'
@echo ' - make all - Build $(OUT_DIR)/$(TARGET_NAME).so'
@echo ' - make dump - Dumps the contents of $(OUT_DIR)/$(TARGET_NAME).so to stdout, requires greadelf and rustfilt'
@echo ''
.PHONY: $(INSTALL_SH)
$(INSTALL_SH):
$(_@)$(INSTALL_SH)
.PRECIOUS: $(OUT_DIR)/%.ll
$(OUT_DIR)/%.ll: $(SRC_DIR)/*
@echo "[cargo] $@ ($<)"
$(_@)mkdir -p $(OUT_DIR)
$(_@)rm -f $(CARGO_OUT_DIR)/deps/$(TARGET_NAME)-*.ll
$(_@)export CARGO_INCREMENTAL=0; $(CARGO) $(CARGO_FLAGS)
$(_@)cp $(CARGO_OUT_DIR)/deps/$(TARGET_NAME)-*.ll $(OUT_DIR)/$(TARGET_NAME).ll
.PRECIOUS: $(OUT_DIR)/%.o
$(OUT_DIR)/%.o: $(OUT_DIR)/%.ll $(INSTALL_SH)
@echo "[llc] $@ ($<)"
$(_@)$(LLC) $(LLC_FLAGS) -o $@ $<
$(_@)$(OBJ_COPY) $(OBJ_COPY_FLAGS) $@
.PRECIOUS: $(OUT_DIR)/%.so
$(OUT_DIR)/%.so: $(OUT_DIR)/%.o $(INSTALL_SH)
@echo "[lld] $@ ($<)"
$(_@)$(LLD) $(LLD_FLAGS) -o $@ $<
-include $(wildcard $(OUT_DIR)/$(TARGET_NAME).d)
define \n
endef
all: $(OUT_DIR)/$(TARGET_NAME).so
# Warning: Do not build as part of install (e.g. install must not depend
# on $(TARGET_NAME).so) doing so will deadlock cargo due to recrusive
# calls to cargo
install:
$(_@)mkdir -p $(INSTALL_DIR)
$(_@)cp $(OUT_DIR)/$(TARGET_NAME).so $(INSTALL_DIR)
dump: $(OUT_DIR)/$(TARGET_NAME).so
$(_@)greadelf -aW $(OUT_DIR)/$(TARGET_NAME).so | rustfilt
$(_@)$(OBJ_DUMP) -disassemble -source $(OUT_DIR)/$(TARGET_NAME).so | rustfilt
test:
cargo test -- --test-threads 1
clean:
$(_@)rm -rf $(OUT_DIR)
cargo clean

View File

@ -7,6 +7,17 @@ mod solana_sdk;
use solana_sdk::*;
struct SStruct {
x: u64,
y: u64,
z: u64,
}
#[inline(never)]
fn return_sstruct() -> SStruct {
SStruct { x: 1, y: 2, z: 3 }
}
fn process(ka: &mut [SolKeyedAccount], data: &[u8], info: &SolClusterInfo) -> bool {
sol_log("Tick height:");
sol_log_64(info.tick_height, 0, 0, 0, 0);
@ -18,5 +29,27 @@ fn process(ka: &mut [SolKeyedAccount], data: &[u8], info: &SolClusterInfo) -> bo
// programs will have specific requirements so they can do their work.
sol_log("Account keys and instruction input data:");
sol_log_params(ka, data);
{
// Test - use core methods, unwrap
// valid bytes, in a stack-allocated array
let sparkle_heart = [240, 159, 146, 150];
let result_str = core::str::from_utf8(&sparkle_heart).unwrap();
sol_log_64(0, 0, 0, 0, result_str.len() as u64);
sol_log(result_str);
assert_eq!("💖", result_str);
}
{
// Test - struct return
let s = return_sstruct();
sol_log_64(0, 0, s.x, s.y, s.z);
assert_eq!(s.x + s.y + s.z, 6);
}
sol_log("Success");
true
}

View File

@ -1,39 +1,80 @@
//! @brief Solana Rust-based BPF program utility functions and types
extern crate heapless;
// extern crate heapless;
use self::heapless::consts::*;
use self::heapless::String; // fixed capacity `std::Vec` // type level integer used to specify capacity
// use self::heapless::consts::*;
// use self::heapless::String; // fixed capacity `std::Vec` // type level integer used to specify capacity
#[cfg(test)]
use self::tests::process;
use core::mem::size_of;
use core::panic::PanicInfo;
use core::slice::from_raw_parts;
#[cfg(not(test))]
use process;
// Panic handling
extern "C" {
pub fn sol_panic_() -> !;
}
#[panic_handler]
fn panic(_info: &PanicInfo) -> ! {
sol_log("Panic!");
// TODO rashes! sol_log(_info.payload().downcast_ref::<&str>().unwrap());
if let Some(location) = _info.location() {
if !location.file().is_empty() {
// TODO location.file() returns empty str, if we get here its been fixed
sol_log(location.file());
sol_log("location.file() is fixed!!");
unsafe {
sol_panic_();
}
}
sol_log_64(0, 0, 0, location.line() as u64, location.column() as u64);
} else {
sol_log("Panic! but could not get location information");
}
unsafe {
sol_panic_();
}
}
extern "C" {
fn sol_log_(message: *const u8);
}
/// Helper function that prints a string to stdout
#[inline(never)] // stack intensive, block inline so everyone does not incur
pub fn sol_log(message: &str) {
let mut c_string: String<U256> = String::new();
if message.len() < 256 {
if c_string.push_str(message).is_err() {
c_string
.push_str("Attempted to log a malformed string\0")
.is_ok();
// TODO This is extremely slow, do something better
let mut buf: [u8; 128] = [0; 128];
for (i, b) in message.as_bytes().iter().enumerate() {
if i >= 126 {
break;
}
if c_string.push('\0').is_err() {
c_string.push_str("Failed to log string\0").is_ok();
};
} else {
c_string
.push_str("Attempted to log a string that is too long\0")
.is_ok();
buf[i] = *b;
}
unsafe {
sol_log_(c_string.as_bytes().as_ptr());
sol_log_(buf.as_ptr());
}
// let mut c_string: String<U256> = String::new();
// if message.len() < 256 {
// if c_string.push_str(message).is_err() {
// c_string
// .push_str("Attempted to log a malformed string\0")
// .is_ok();
// }
// if c_string.push('\0').is_err() {
// c_string.push_str("Failed to log string\0").is_ok();
// };
// } else {
// c_string
// .push_str("Attempted to log a string that is too long\0")
// .is_ok();
// }
// unsafe {
// sol_log_(message.as_ptr());
// }
}
extern "C" {

View File

@ -60,43 +60,50 @@ fn main() {
let bpf_rust = !env::var("CARGO_FEATURE_BPF_RUST").is_err();
if bpf_rust {
let install_dir = "INSTALL_DIR=../../../../target/".to_string()
+ &env::var("PROFILE").unwrap()
+ &"/bpf".to_string();
let install_dir =
"../../../../target/".to_string() + &env::var("PROFILE").unwrap() + &"/bpf".to_string();
if !Path::new("../../bpf/rust/noop/out/solana_bpf_rust_noop.so").is_file() {
if !Path::new(
"../../bpf/rust/noop/target/bpfel-unknown-unknown/release/solana_bpf_rust_noop.so",
)
.is_file()
{
// Cannot build Rust BPF programs as part of main build because
// to build it requires calling Cargo with different parameters which
// would deadlock due to recursive cargo calls
panic!(
"solana_bpf_rust_noop.so not found, you must manually run \
`make all` in programs/bpf/rust/noop to build it"
`build.sh` in programs/bpf/rust/noop to build it"
);
}
rerun_if_changed(
&[
"../../bpf/rust/noop/bpf.ld",
"../../bpf/rust/noop/makefile",
"../../bpf/rust/noop/out/solana_bpf_rust_noop.so",
"../../bpf/rust/noop/build.sh",
"../../bpf/rust/noop/Cargo.toml",
"../../bpf/rust/noop/target/bpfel-unknown-unknown/release/solana_bpf_rust_noop.so",
],
&[],
&["../../bpf/rust/noop/src"],
);
println!(
"cargo:warning=(not a warning) Installing Rust-based BPF program: solana_bpf_rust_noop"
);
let status = Command::new("make")
let status = Command::new("mkdir")
.current_dir("../../bpf/rust/noop")
.arg("install")
.arg("V=1")
.arg("OUT_DIR=out")
.arg("-p")
.arg(&install_dir)
.status()
.expect(
"solana_bpf_rust_noop.so not found, you must manually run \
`make all` in its program directory",
);
.expect("Unable to create BPF install directory");
assert!(status.success());
let status = Command::new("cp")
.current_dir("../../bpf/rust/noop")
.arg("target/bpfel-unknown-unknown/release/solana_bpf_rust_noop.so")
.arg(&install_dir)
.status()
.expect("Failed to copy solana_rust_bpf_noop.so to install directory");
assert!(status.success());
}
}