├── .gitignore ├── CMakeLists.txt ├── Dockerfile ├── LICENSE ├── README.md ├── format.sh ├── inc ├── client.h ├── config.h ├── log_disk.h ├── malloc.h ├── memory.h ├── object_log.h └── store.h ├── java ├── JlightningClient.cc ├── jlightning │ ├── JlightningClient.java │ └── Test.java └── jlightning_JlightningClient.h ├── python ├── Makefile ├── _lightning_client.pxd ├── _lightning_client.pyx ├── setup.py └── test_client.py ├── script ├── recovery_latency.sh ├── subscription_latency.sh └── throughput.sh ├── src ├── client.cc ├── log_disk.cc ├── malloc.cc ├── object_log.cc └── store.cc ├── test ├── benchmark.cc ├── create_latency.cc ├── create_object.cc ├── mp_benchmark.cc ├── subscribe.cc └── test_cleanup.cc └── verifier ├── CMakeLists.txt ├── exec ├── print_all_funcs.cc ├── print_class_fields.cc └── verify_num_logwrite.cc ├── inc ├── abstract-functions.h ├── data-structures.h ├── executor.h ├── llvm-helpers.h ├── llvm-incl.h ├── logwrite_bound.h ├── symbolic-expr.h ├── utils.h └── z3-gen.h ├── src ├── abstract-functions.cc ├── data-structures.cc ├── executor.cc ├── llvm-helpers.cc ├── logwrite_bound.cc ├── symbolic-expr.cc ├── utils.cc └── z3-gen.cc └── undo_log.dfy /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | *.smod 22 | 23 | # Compiled Static libraries 24 | *.lai 25 | *.la 26 | *.a 27 | *.lib 28 | 29 | # Executables 30 | *.exe 31 | *.out 32 | *.app 33 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10) 2 | 3 | project(Lightning) 4 | 5 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -lrt") 6 | set(CMAKE_POSITION_INDEPENDENT_CODE ON) 7 | 8 | set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -O0 -g") 9 | set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O3") 10 | 11 | set(SRC_DIR "${PROJECT_SOURCE_DIR}/src") 12 | include_directories("${PROJECT_SOURCE_DIR}/inc") 13 | 14 | file(GLOB LIB_SRC 15 | "${SRC_DIR}/log_disk.cc" 16 | "${SRC_DIR}/malloc.cc" 17 | "${SRC_DIR}/object_log.cc" 18 | ) 19 | 20 | add_library(lightning STATIC ${LIB_SRC}) 21 | target_link_libraries(lightning "-lrt -lpthread") 22 | 23 | add_executable(store "${SRC_DIR}/store.cc") 24 | target_link_libraries(store lightning) 25 | 26 | file(GLOB TEST_SRC 27 | "${PROJECT_SOURCE_DIR}/test/*.cc" 28 | ) 29 | 30 | foreach(test_file ${TEST_SRC}) 31 | get_filename_component(prog_name ${test_file} NAME_WE) 32 | add_executable(${prog_name} "${SRC_DIR}/client.cc" ${test_file}) 33 | target_link_libraries(${prog_name} lightning) 34 | endforeach(test_file ${TEST_SRC}) 35 | 36 | option(VERIFIER "Build the verifier" OFF) 37 | 38 | if (VERIFIER) 39 | 40 | foreach(src_file ${LIB_SRC} "${SRC_DIR}/client.cc" "${SRC_DIR}/store.cc") 41 | get_filename_component(prog_name ${src_file} NAME_WE) 42 | execute_process ( 43 | COMMAND bash -c "clang++-6.0 -I${PROJECT_SOURCE_DIR}/inc -g -S -emit-llvm -o ${prog_name}.ll ${SRC_DIR}/${prog_name}.cc" 44 | ) 45 | endforeach(src_file ${LIB_SRC} "${SRC_DIR}/client.cc" "${SRC_DIR}/store.cc") 46 | 47 | add_subdirectory(verifier) 48 | 49 | endif (VERIFIER) 50 | 51 | option(JAVA_CLIENT "Build the Java client" OFF) 52 | 53 | if (JAVA_CLIENT) 54 | find_package(JNI REQUIRED) 55 | include_directories(${JNI_INCLUDE_DIRS}) 56 | 57 | add_library(jlightning SHARED ${LIB_SRC} "${SRC_DIR}/client.cc" "${PROJECT_SOURCE_DIR}/java/JlightningClient.cc") 58 | target_link_libraries(jlightning "-lrt -lpthread") 59 | 60 | endif (JAVA_CLIENT) 61 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:20.04 2 | COPY . /lightning 3 | RUN apt update 4 | RUN DEBIAN_FRONTEND=noninteractive apt-get install -y build-essential cmake clang-6.0 llvm-9-dev wget unzip python3-dev libz3-dev libelf-dev libboost-dev gdb vim tmux 5 | 6 | WORKDIR /tmp 7 | RUN wget https://github.com/dafny-lang/dafny/releases/download/v3.3.0/dafny-3.3.0-x64-ubuntu-16.04.zip 8 | RUN unzip dafny-3.3.0-x64-ubuntu-16.04.zip 9 | 10 | WORKDIR /lightning 11 | CMD bash 12 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Duke University, University of California - Berkeley, University of Washington 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Lightning In-Memory Object Store 2 | Lightning is a high-performance in-memory object store. Compared to traditional in-memory object stores (e.g., Redis, Memcached, Plasma), Lightning does not have inter-process communication (IPC) overheads on client operations (e.g., object creation, object fetching, object deletion). 3 | 4 | Our VLDB 2022 paper (https://danyangzhuo.com/papers/VLDB22-Lightning.pdf) describes the technical details. Please email Danyang Zhuo (danyang@cs.duke.edu) if you have any question. 5 | 6 | This is just a research prototype. Please don't use it in production systems. 7 | 8 | ## Requirement 9 | Lightning runs on the following configuration: 10 | * Linux (4.15.0) 11 | * Clang (6.0.0) 12 | * Boost (1.65.1) 13 | * Z3 (4.8.9) 14 | * Mono (6.8.0) 15 | * Dafny (2.3.0) 16 | 17 | ## Docker 18 | We suggest you use docker to construct the environment for compiling and running Lightning. We provide a dockerfile to simply this process. 19 | 20 | First, you need to build the docker image. 21 | ```bash 22 | docker build -t lightning . 23 | ``` 24 | 25 | Second, you need to instantiate a container. You need to enlarge the size of maximum shm the container use to at least 10G. 26 | ```bash 27 | docker run -it --rm --shm-size=10g lightning 28 | ``` 29 | 30 | ## Build 31 | ```bash 32 | mkdir build 33 | cd build 34 | cmake -DVERIFIER=ON .. 35 | make -j 36 | ``` 37 | If you don't want to build the verifier, you can delete the "-DVERIFIER=ON" flag. 38 | 39 | ## Run 40 | ```bash 41 | ./store 42 | ``` 43 | In another terminal, 44 | ```bash 45 | ./benchmark 46 | ``` 47 | 48 | ## Verify 49 | We verify Lightning's crash fault isolation property in two steps. 50 | 51 | ### Step #1: Verify the correctness of log implementation 52 | ```bash 53 | dafny ../verifier/undo_log.dfy 54 | ``` 55 | 56 | ### Step #2: Verify that Lightning's C++ implementation uses the log correctly 57 | ```bash 58 | ./verifier/verify_num_logwrite 59 | ``` 60 | This will take around 10-15 minutes. 61 | 62 | ## Build the Java Client 63 | ```bash 64 | mkdir build 65 | cd build 66 | cmake -DJAVA_CLIENT=ON .. 67 | make -j 68 | ``` 69 | 70 | ## Build the Python Client 71 | ```bash 72 | cd python 73 | make 74 | ``` 75 | -------------------------------------------------------------------------------- /format.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | clang-format -i src/*.cc inc/*.h java/*.cc verifier/exec/*.cc verifier/inc/*.h verifier/src/*.cc 3 | -------------------------------------------------------------------------------- /inc/client.h: -------------------------------------------------------------------------------- 1 | #ifndef CLIENT_H 2 | #define CLIENT_H 3 | #include 4 | #include 5 | 6 | #include "config.h" 7 | #include "log_disk.h" 8 | #include "malloc.h" 9 | #include "object_log.h" 10 | 11 | class LightningClient { 12 | public: 13 | LightningClient(const std::string &store_socket, const std::string &password); 14 | 15 | int MultiPut(uint64_t object_id, std::vector fields, 16 | std::vector subobject_sizes, 17 | std::vector subobjects); 18 | 19 | int MultiGet(uint64_t object_id, std::vector in_fields, 20 | std::vector *out_field_sizes, 21 | std::vector *out_fields, 22 | std::vector *subobject_sizes, 23 | std::vector *subobjects); 24 | 25 | int MultiUpdate(uint64_t object_id, std::vector fields, 26 | std::vector subobject_sizes, 27 | std::vector subobjects); 28 | 29 | int Create(uint64_t object_id, uint8_t **ptr, size_t size); 30 | 31 | int Seal(uint64_t object_id); 32 | 33 | int Get(uint64_t object_id, uint8_t **ptr, size_t *size); 34 | 35 | int Release(uint64_t object_id); 36 | 37 | int Delete(uint64_t object_id); 38 | 39 | int Subscribe(uint64_t object_id); 40 | 41 | private: 42 | int store_conn_; 43 | int store_fd_; 44 | int log_fd_; 45 | 46 | LightningStoreHeader *header_; 47 | int size_; 48 | MemAllocator *allocator_; 49 | 50 | int64_t alloc_object_entry(); 51 | void dealloc_object_entry(int64_t object_index); 52 | 53 | int64_t find_object(uint64_t object_id); 54 | int create_internal(uint64_t object_id, sm_offset *offset, size_t size); 55 | int get_internal(uint64_t object_id, sm_offset *ptr, size_t *size); 56 | int seal_internal(uint64_t object_id); 57 | int delete_internal(uint64_t object_id); 58 | int subscribe_internal(uint64_t object_id, sem_t **sem, bool *wait); 59 | void init_mpk(); 60 | 61 | uint64_t object_id_from_str(const std::string &s); 62 | 63 | uint8_t *base_; 64 | 65 | int object_log_fd_; 66 | uint8_t *object_log_base_; 67 | ObjectLog *object_log_; 68 | pid_t pid_; 69 | 70 | UndoLogDisk *disk_; 71 | }; 72 | 73 | #endif // CLIENT_H 74 | -------------------------------------------------------------------------------- /inc/config.h: -------------------------------------------------------------------------------- 1 | #define USE_MPK 2 | -------------------------------------------------------------------------------- /inc/log_disk.h: -------------------------------------------------------------------------------- 1 | #ifndef UNDO_DISK_H 2 | #define UNDO_DISK_H 3 | 4 | #include 5 | 6 | #include "memory.h" 7 | 8 | struct LogEntry { 9 | sm_offset offset; 10 | int64_t value; 11 | }; 12 | 13 | class UndoLogDisk { 14 | public: 15 | UndoLogDisk(size_t log_size, uint8_t *shm_base, size_t shm_size); 16 | 17 | void BeginTx(); 18 | void CommitTx(); 19 | 20 | void Write(sm_offset offset, uint64_t value); 21 | 22 | protected: 23 | int log_fd_; 24 | 25 | uint8_t *shm_base_; 26 | size_t shm_size_; 27 | uint8_t *log_base_; 28 | size_t log_size_; 29 | }; 30 | 31 | #define LOGGED_WRITE(lval, rval, hdr_ptr, log_ptr) \ 32 | do { \ 33 | sm_offset offset = (uint8_t *)(&(lval)) - (uint8_t *)(hdr_ptr); \ 34 | log_ptr->Write(offset, rval); \ 35 | } while (false) 36 | 37 | #endif // UNDO_DISK_H 38 | -------------------------------------------------------------------------------- /inc/malloc.h: -------------------------------------------------------------------------------- 1 | #ifndef MALLOC_H 2 | #define MALLOC_H 3 | 4 | #include "log_disk.h" 5 | #include "memory.h" 6 | 7 | class MemAllocator { 8 | public: 9 | MemAllocator(LightningStoreHeader *header, UndoLogDisk *disk); 10 | void Init(sm_offset start, size_t size); 11 | 12 | sm_offset MallocShared(size_t size); 13 | void FreeShared(sm_offset offset); 14 | 15 | void PrintAvalaibleMemory(); 16 | 17 | // for crash recovery 18 | void FreeSharedNoLog(sm_offset offset); 19 | 20 | private: 21 | void split_memory_to_free_lists(sm_offset offset, size_t size); 22 | int64_t create_block(sm_offset ptr, size_t size); 23 | void add_to_free_list(int index, int64_t mem_entry_index); 24 | int64_t create_block_nolog(sm_offset ptr, size_t size); 25 | void add_to_free_list_nolog(int index, int64_t mem_entry_index); 26 | int64_t remove_from_free_list(int index); 27 | int64_t get_free_block(int index); 28 | void remove_block(int index, int64_t mem_entry_index); 29 | void remove_block_nolog(int index, int64_t mem_entry_index); 30 | int64_t separate_buddy(int64_t mem_index, int index); 31 | int64_t merge_blocks(int64_t mem_entry_index1, int64_t mem_entry_index2, 32 | int index); 33 | int64_t merge_blocks_nolog(int64_t mem_entry_index1, int64_t mem_entry_index2, 34 | int index); 35 | 36 | int64_t allocate_memory_entry(); 37 | int64_t allocate_memory_entry_nolog(); 38 | void deallocate_memory_entry(int64_t mem_entry_index); 39 | void deallocate_memory_entry_nolog(int64_t mem_entry_index); 40 | 41 | LightningStoreHeader *header_; 42 | FreeList *free_list_; 43 | uint8_t *base_; 44 | UndoLogDisk *disk_; 45 | }; 46 | 47 | #endif // MALLOC_H 48 | -------------------------------------------------------------------------------- /inc/memory.h: -------------------------------------------------------------------------------- 1 | #ifndef MEMORY_H 2 | #define MEMORY_H 3 | 4 | #include 5 | #include 6 | 7 | #define MINIMAL_BLOCK_SIZE_LOG 10 8 | #define MAXIMUM_BLOCK_SIZE_LOG 30 9 | #define HASHMAP_SIZE 65536 10 | 11 | #define MAX_NUM_OBJECTS 100000 12 | 13 | typedef int64_t sm_offset; 14 | 15 | struct MemoryEntry { 16 | int64_t free_list_next; 17 | int64_t in_use; 18 | sm_offset offset; 19 | size_t size; 20 | int64_t prev; 21 | int64_t next; 22 | int64_t buddy[32]; 23 | }; 24 | 25 | struct MemoryHeader { 26 | int64_t index; 27 | }; 28 | 29 | struct FreeList { 30 | int64_t free_list_head[32]; 31 | }; 32 | 33 | struct ObjectEntry { 34 | // 0 35 | uint64_t object_id; 36 | // 8 37 | sm_offset offset; 38 | // 16 39 | size_t size; 40 | // 24 41 | int64_t ref_count; 42 | // 32 43 | int64_t sealed; 44 | // 40 45 | int64_t prev; 46 | // 48 47 | int64_t next; 48 | // 56 49 | int64_t num_waiters; 50 | // 64 51 | int64_t free_list_next; 52 | // 72 53 | sem_t sem; 54 | } __attribute__((aligned(8))); 55 | 56 | struct HashEntry { 57 | int64_t object_list; 58 | }; 59 | 60 | struct HashMap { 61 | HashEntry hash_entries[HASHMAP_SIZE]; 62 | }; 63 | 64 | struct LightningStoreHeader { 65 | volatile int lock_flag = 0; 66 | MemoryEntry memory_entries[MAX_NUM_OBJECTS]; 67 | ObjectEntry object_entries[MAX_NUM_OBJECTS]; 68 | int64_t memory_entry_free_list; 69 | int64_t object_entry_free_list; 70 | FreeList free_list; 71 | HashMap hashmap; 72 | }; 73 | 74 | #endif // MEMORY_H 75 | -------------------------------------------------------------------------------- /inc/object_log.h: -------------------------------------------------------------------------------- 1 | #ifndef LOG_H 2 | #define LOG_H 3 | #include 4 | #include 5 | 6 | #include "log_disk.h" 7 | #include "memory.h" 8 | 9 | #define OBJECT_LOG_SIZE 1048576 10 | 11 | struct LogObjectEntry { 12 | uint64_t object_id; 13 | bool in_use; 14 | }; 15 | 16 | class ObjectLog { 17 | public: 18 | ObjectLog(uint8_t *object_log_base, sm_offset object_log_offset, 19 | UndoLogDisk *disk); 20 | 21 | void OpenObject(int64_t object_id); 22 | void CloseObject(int64_t object_id); 23 | 24 | private: 25 | int object_log_fd_; 26 | 27 | sm_offset object_log_offset_; 28 | uint8_t *object_log_base_; 29 | 30 | UndoLogDisk *disk_; 31 | 32 | std::unordered_map object_cache_; 33 | 34 | int64_t find_object(int64_t object_id); 35 | void erase_object(int64_t object_id); 36 | void insert_object(int64_t object_id, int64_t index); 37 | int64_t find_new_entry(int64_t object_id); 38 | }; 39 | 40 | #endif // LOG_H 41 | -------------------------------------------------------------------------------- /inc/store.h: -------------------------------------------------------------------------------- 1 | #ifndef STORE_H 2 | #define STORE_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "config.h" 9 | #include "malloc.h" 10 | 11 | class LightningStore { 12 | public: 13 | LightningStore(const std::string &unix_socket, int size); 14 | void Run(); 15 | 16 | private: 17 | void monitor(); 18 | void listener(); 19 | 20 | void recover(uint8_t *sm, uint8_t *log, uint8_t *object_log, pid_t pid); 21 | 22 | std::string unix_socket_; 23 | int size_; 24 | int store_fd_; 25 | LightningStoreHeader *store_header_; 26 | 27 | std::mutex client_lock_; 28 | std::unordered_set clients_; 29 | MemAllocator *allocator_; 30 | 31 | int64_t find_object(uint64_t object_id); 32 | int release_object(uint64_t object_id); 33 | 34 | int64_t alloc_object_entry(); 35 | void dealloc_object_entry(int64_t object_index); 36 | int create_object(uint64_t object_id, sm_offset *offset, size_t size); 37 | int get_object(uint64_t object_id, sm_offset *ptr, size_t *size); 38 | int seal_object(uint64_t object_id); 39 | int delete_object(uint64_t object_id); 40 | }; 41 | 42 | #endif // STORE_H 43 | -------------------------------------------------------------------------------- /java/JlightningClient.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "client.h" 8 | #include "jlightning_JlightningClient.h" 9 | 10 | JNIEXPORT jlong JNICALL Java_jlightning_JlightningClient_connect( 11 | JNIEnv *env, jobject thisObj, jstring unix_socket, jstring password) { 12 | const char *unix_socket_s = env->GetStringUTFChars(unix_socket, nullptr); 13 | std::string unix_socket_string = std::string(unix_socket_s); 14 | const char *password_s = env->GetStringUTFChars(password, nullptr); 15 | std::string password_string = std::string(password_s); 16 | 17 | return reinterpret_cast( 18 | new LightningClient(unix_socket_string, password_string)); 19 | } 20 | 21 | JNIEXPORT jobject JNICALL Java_jlightning_JlightningClient_create( 22 | JNIEnv *env, jobject thisObj, jlong conn, jlong object_id, jint size) { 23 | LightningClient *client = reinterpret_cast(conn); 24 | uint8_t *value; 25 | client->Create(object_id, &value, size); 26 | return env->NewDirectByteBuffer(value, size); 27 | } 28 | 29 | JNIEXPORT void JNICALL Java_jlightning_JlightningClient_seal(JNIEnv *env, 30 | jobject thisObj, 31 | jlong conn, 32 | jlong object_id) { 33 | LightningClient *client = reinterpret_cast(conn); 34 | client->Seal(object_id); 35 | } 36 | 37 | JNIEXPORT jobject JNICALL Java_jlightning_JlightningClient_get( 38 | JNIEnv *env, jobject thisObj, jlong conn, jlong object_id) { 39 | LightningClient *client = reinterpret_cast(conn); 40 | uint8_t *value; 41 | size_t size; 42 | client->Get(object_id, &value, &size); 43 | return env->NewDirectByteBuffer(value, size); 44 | } 45 | 46 | JNIEXPORT void JNICALL Java_jlightning_JlightningClient_release( 47 | JNIEnv *env, jobject thisObj, jlong conn, jlong object_id) { 48 | LightningClient *client = reinterpret_cast(conn); 49 | client->Release(object_id); 50 | } 51 | 52 | JNIEXPORT void JNICALL Java_jlightning_JlightningClient_delete( 53 | JNIEnv *env, jobject thisObj, jlong conn, jlong object_id) { 54 | LightningClient *client = reinterpret_cast(conn); 55 | client->Delete(object_id); 56 | } 57 | 58 | JNIEXPORT void JNICALL Java_jlightning_JlightningClient_multiput( 59 | JNIEnv *env, jobject thisObj, jlong conn, jlong object_id, 60 | jobjectArray fields, jobjectArray values) { 61 | LightningClient *client = reinterpret_cast(conn); 62 | std::vector fields_cpp; 63 | for (int i = 0; i < env->GetArrayLength(fields); i++) { 64 | jstring field_jstring = jstring(env->GetObjectArrayElement(fields, i)); 65 | const char *field_cpp_s = env->GetStringUTFChars(field_jstring, nullptr); 66 | std::string field_cpp_string = std::string(field_cpp_s); 67 | fields_cpp.push_back(field_cpp_string); 68 | } 69 | 70 | std::vector subobject_sizes; 71 | std::vector values_cpp; 72 | for (int i = 0; i < env->GetArrayLength(values); i++) { 73 | jbyteArray value = jbyteArray(env->GetObjectArrayElement(values, i)); 74 | subobject_sizes.push_back(env->GetArrayLength(value)); 75 | values_cpp.push_back((uint8_t *)env->GetPrimitiveArrayCritical(value, 0)); 76 | } 77 | 78 | client->MultiPut(object_id, fields_cpp, subobject_sizes, values_cpp); 79 | for (int i = 0; i < env->GetArrayLength(values); i++) { 80 | jbyteArray value = jbyteArray(env->GetObjectArrayElement(values, i)); 81 | env->ReleasePrimitiveArrayCritical(value, values_cpp[i], 0); 82 | } 83 | } 84 | 85 | JNIEXPORT void JNICALL Java_jlightning_JlightningClient_multiupdate( 86 | JNIEnv *env, jobject thisObj, jlong conn, jlong object_id, 87 | jobjectArray fields, jobjectArray values) { 88 | LightningClient *client = reinterpret_cast(conn); 89 | std::vector fields_cpp; 90 | for (int i = 0; i < env->GetArrayLength(fields); i++) { 91 | jstring field_jstring = jstring(env->GetObjectArrayElement(fields, i)); 92 | const char *field_cpp_s = env->GetStringUTFChars(field_jstring, nullptr); 93 | std::string field_cpp_string = std::string(field_cpp_s); 94 | fields_cpp.push_back(field_cpp_string); 95 | } 96 | 97 | std::vector subobject_sizes; 98 | std::vector values_cpp; 99 | for (int i = 0; i < env->GetArrayLength(values); i++) { 100 | jbyteArray value = jbyteArray(env->GetObjectArrayElement(values, i)); 101 | subobject_sizes.push_back(env->GetArrayLength(value)); 102 | values_cpp.push_back((uint8_t *)env->GetPrimitiveArrayCritical(value, 0)); 103 | } 104 | 105 | client->MultiUpdate(object_id, fields_cpp, subobject_sizes, values_cpp); 106 | for (int i = 0; i < env->GetArrayLength(values); i++) { 107 | jbyteArray value = jbyteArray(env->GetObjectArrayElement(values, i)); 108 | env->ReleasePrimitiveArrayCritical(value, values_cpp[i], 0); 109 | } 110 | } 111 | 112 | JNIEXPORT jlongArray JNICALL Java_jlightning_JlightningClient_multiget( 113 | JNIEnv *env, jobject thisObj, jlong conn, jlong object_id, 114 | jobjectArray fields) { 115 | 116 | LightningClient *client = reinterpret_cast(conn); 117 | std::vector fields_cpp; 118 | for (int i = 0; i < env->GetArrayLength(fields); i++) { 119 | jstring field_jstring = jstring(env->GetObjectArrayElement(fields, i)); 120 | const char *field_cpp_s = env->GetStringUTFChars(field_jstring, nullptr); 121 | std::string field_cpp_string = std::string(field_cpp_s); 122 | fields_cpp.push_back(field_cpp_string); 123 | } 124 | 125 | std::vector field_sizes; 126 | std::vector outfields; 127 | std::vector subobject_sizes; 128 | std::vector values_cpp; 129 | client->MultiGet(object_id, fields_cpp, &field_sizes, &outfields, 130 | &subobject_sizes, &values_cpp); 131 | 132 | int size = field_sizes.size(); 133 | assert(size == outfields.size()); 134 | assert(size == subobject_sizes.size()); 135 | assert(size == values_cpp.size()); 136 | 137 | jlongArray ret = env->NewLongArray(size * 4); 138 | jlong *ptr = env->GetLongArrayElements(ret, 0); 139 | 140 | for (int i = 0; i < size; i++) { 141 | ptr[4 * i] = (jlong)field_sizes[i]; 142 | ptr[4 * i + 1] = (jlong)outfields[i]; 143 | ptr[4 * i + 2] = (jlong)subobject_sizes[i]; 144 | ptr[4 * i + 3] = (jlong)values_cpp[i]; 145 | } 146 | 147 | env->ReleaseLongArrayElements(ret, ptr, 0); 148 | return ret; 149 | } 150 | 151 | JNIEXPORT jbyte JNICALL Java_jlightning_JlightningClient_getbyte( 152 | JNIEnv *env, jobject thisObj, jlong addr) { 153 | return jbyte(*(char *)addr); 154 | } 155 | 156 | JNIEXPORT void JNICALL Java_jlightning_JlightningClient_getbytes( 157 | JNIEnv *env, jobject thisObj, jbyteArray target, jlong start, jlong addr, 158 | jlong size) { 159 | env->SetByteArrayRegion(target, (jsize)start, (jsize)size, 160 | (const jbyte *)addr); 161 | return; 162 | } 163 | -------------------------------------------------------------------------------- /java/jlightning/JlightningClient.java: -------------------------------------------------------------------------------- 1 | package jlightning; 2 | 3 | import java.nio.ByteBuffer; 4 | 5 | /** 6 | * Lightning Client. 7 | * 8 | */ 9 | public class JlightningClient { 10 | static { 11 | System.loadLibrary("jlightning"); 12 | } 13 | 14 | private long conn; 15 | 16 | private native long connect(String socket, String password); 17 | private native ByteBuffer create(long c, long id, int size); 18 | private native void seal(long c, long id); 19 | private native ByteBuffer get(long c, long id); 20 | private native void release(long c, long id); 21 | private native void delete(long c, long id); 22 | private native void multiput(long c, long id, String[] fields, Object[] values); 23 | private native void multiupdate(long c, long id, String[] fields, Object[] values); 24 | private native long[] multiget(long c, long id, String[] fields); 25 | 26 | public native byte getbyte(long addr); 27 | public native void getbytes(byte[] target, long start, long addr, long size); 28 | 29 | public JlightningClient(String socket, String password) { 30 | this.conn = connect(socket, password); 31 | } 32 | 33 | public ByteBuffer create(long id, int size) { 34 | return create(this.conn, id, size); 35 | } 36 | 37 | public void seal(long id) { 38 | seal(this.conn, id); 39 | return; 40 | } 41 | 42 | public ByteBuffer get(long id) { 43 | return get(this.conn, id); 44 | } 45 | 46 | public void release(long id) { 47 | release(this.conn, id); 48 | return; 49 | } 50 | 51 | public void delete(long id) { 52 | delete(this.conn, id); 53 | return; 54 | } 55 | 56 | public void multiput(long id, String[] fields, Object[] values) { 57 | multiput(this.conn, id, fields, values); 58 | return; 59 | } 60 | 61 | public void multiupdate(long id, String[] fields, Object[] values) { 62 | multiupdate(this.conn, id, fields, values); 63 | return; 64 | } 65 | 66 | public long[] multiget(long id, String[] fields) { 67 | return multiget(this.conn, id, fields); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /java/jlightning/Test.java: -------------------------------------------------------------------------------- 1 | package jlightning; 2 | 3 | import java.util.Arrays; 4 | import java.nio.ByteBuffer; 5 | 6 | public class Test { 7 | public static void test(JlightningClient client, int object_size) { 8 | byte[] b = new byte[object_size]; 9 | Arrays.fill(b, (byte) 1); 10 | int len = b.length; 11 | 12 | System.out.print(len); 13 | System.out.print(","); 14 | 15 | int num_tests = 100; 16 | 17 | long start = System.nanoTime(); 18 | for (long i = 0; i < num_tests; i++) { 19 | ByteBuffer buf = client.create(i, len); 20 | buf.put(b); 21 | client.seal(i); 22 | } 23 | 24 | long end = System.nanoTime(); 25 | 26 | System.out.print((end - start)/num_tests/1e9); 27 | System.out.print(","); 28 | 29 | start = System.nanoTime(); 30 | for (long i = 0; i < num_tests; i++) { 31 | ByteBuffer getbuf = client.get(i); 32 | } 33 | 34 | end = System.nanoTime(); 35 | 36 | System.out.print((end - start)/num_tests/1e9); 37 | System.out.print(","); 38 | 39 | start = System.nanoTime(); 40 | for (long i = 0; i < num_tests; i++) { 41 | client.delete(i); 42 | } 43 | 44 | end = System.nanoTime(); 45 | 46 | System.out.print((end - start)/num_tests/1e9); 47 | System.out.println(); 48 | } 49 | 50 | public static void main(String[] args) { 51 | JlightningClient client = new JlightningClient("/tmp/lightning", "password"); 52 | for (int i = 0; i < 100; i++) { 53 | for (int object_size = 1024 * 1024; object_size >= 16; object_size /=2) { 54 | test(client, object_size); 55 | } 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /java/jlightning_JlightningClient.h: -------------------------------------------------------------------------------- 1 | /* DO NOT EDIT THIS FILE - it is machine generated */ 2 | #include 3 | /* Header for class jlightning_JlightningClient */ 4 | 5 | #ifndef _Included_jlightning_JlightningClient 6 | #define _Included_jlightning_JlightningClient 7 | #ifdef __cplusplus 8 | extern "C" { 9 | #endif 10 | /* 11 | * Class: jlightning_JlightningClient 12 | * Method: connect 13 | * Signature: (Ljava/lang/String;Ljava/lang/String;)J 14 | */ 15 | JNIEXPORT jlong JNICALL Java_jlightning_JlightningClient_connect 16 | (JNIEnv *, jobject, jstring, jstring); 17 | 18 | /* 19 | * Class: jlightning_JlightningClient 20 | * Method: create 21 | * Signature: (JJI)Ljava/nio/ByteBuffer; 22 | */ 23 | JNIEXPORT jobject JNICALL Java_jlightning_JlightningClient_create 24 | (JNIEnv *, jobject, jlong, jlong, jint); 25 | 26 | /* 27 | * Class: jlightning_JlightningClient 28 | * Method: seal 29 | * Signature: (JJ)V 30 | */ 31 | JNIEXPORT void JNICALL Java_jlightning_JlightningClient_seal 32 | (JNIEnv *, jobject, jlong, jlong); 33 | 34 | /* 35 | * Class: jlightning_JlightningClient 36 | * Method: get 37 | * Signature: (JJ)Ljava/nio/ByteBuffer; 38 | */ 39 | JNIEXPORT jobject JNICALL Java_jlightning_JlightningClient_get 40 | (JNIEnv *, jobject, jlong, jlong); 41 | 42 | /* 43 | * Class: jlightning_JlightningClient 44 | * Method: release 45 | * Signature: (JJ)V 46 | */ 47 | JNIEXPORT void JNICALL Java_jlightning_JlightningClient_release 48 | (JNIEnv *, jobject, jlong, jlong); 49 | 50 | /* 51 | * Class: jlightning_JlightningClient 52 | * Method: delete 53 | * Signature: (JJ)V 54 | */ 55 | JNIEXPORT void JNICALL Java_jlightning_JlightningClient_delete 56 | (JNIEnv *, jobject, jlong, jlong); 57 | 58 | /* 59 | * Class: jlightning_JlightningClient 60 | * Method: multiput 61 | * Signature: (JJ[Ljava/lang/String;[Ljava/lang/Object;)V 62 | */ 63 | JNIEXPORT void JNICALL Java_jlightning_JlightningClient_multiput 64 | (JNIEnv *, jobject, jlong, jlong, jobjectArray, jobjectArray); 65 | 66 | /* 67 | * Class: jlightning_JlightningClient 68 | * Method: multiupdate 69 | * Signature: (JJ[Ljava/lang/String;[Ljava/lang/Object;)V 70 | */ 71 | JNIEXPORT void JNICALL Java_jlightning_JlightningClient_multiupdate 72 | (JNIEnv *, jobject, jlong, jlong, jobjectArray, jobjectArray); 73 | 74 | /* 75 | * Class: jlightning_JlightningClient 76 | * Method: multiget 77 | * Signature: (JJ[Ljava/lang/String;)[J 78 | */ 79 | JNIEXPORT jlongArray JNICALL Java_jlightning_JlightningClient_multiget 80 | (JNIEnv *, jobject, jlong, jlong, jobjectArray); 81 | 82 | /* 83 | * Class: jlightning_JlightningClient 84 | * Method: getbyte 85 | * Signature: (J)B 86 | */ 87 | JNIEXPORT jbyte JNICALL Java_jlightning_JlightningClient_getbyte 88 | (JNIEnv *, jobject, jlong); 89 | 90 | /* 91 | * Class: jlightning_JlightningClient 92 | * Method: getbytes 93 | * Signature: ([BJJJ)V 94 | */ 95 | JNIEXPORT void JNICALL Java_jlightning_JlightningClient_getbytes 96 | (JNIEnv *, jobject, jbyteArray, jlong, jlong, jlong); 97 | 98 | #ifdef __cplusplus 99 | } 100 | #endif 101 | #endif 102 | -------------------------------------------------------------------------------- /python/Makefile: -------------------------------------------------------------------------------- 1 | all: py_lightning_client 2 | 3 | py_lightning_client: 4 | python setup.py build_ext --inplace 5 | 6 | clean: 7 | rm -rf *.so *.cpp build 8 | -------------------------------------------------------------------------------- /python/_lightning_client.pxd: -------------------------------------------------------------------------------- 1 | # cython: language_level = 3 2 | 3 | from libcpp cimport bool as c_bool 4 | from libcpp.memory cimport shared_ptr, unique_ptr 5 | from libcpp.string cimport string as c_string 6 | 7 | from libc.stdint cimport uint8_t, int32_t, uint64_t, int64_t, uint32_t 8 | from libcpp.unordered_map cimport unordered_map 9 | from libcpp.vector cimport vector as c_vector 10 | 11 | cdef extern from "../inc/client.h" namespace "" nogil: 12 | cdef cppclass CLightningClient "LightningClient": 13 | CLightningClient(const c_string &store_socket, const c_string &password) 14 | 15 | int Create(uint64_t object_id, uint8_t **ptr, size_t size) 16 | 17 | int Seal(uint64_t object_id) 18 | 19 | int Get(uint64_t object_id, uint8_t **ptr, size_t *size) 20 | 21 | int Release(uint64_t object_id) 22 | 23 | int Delete(uint64_t object_id) 24 | -------------------------------------------------------------------------------- /python/_lightning_client.pyx: -------------------------------------------------------------------------------- 1 | # distutils: language = c++ 2 | # cython: embedsignature = True 3 | # cython: language_level = 3 4 | 5 | from libcpp cimport bool as c_bool 6 | from libcpp.memory cimport shared_ptr, unique_ptr 7 | from libcpp.string cimport string as c_string 8 | 9 | from libc.stdint cimport uint8_t, int32_t, uint64_t, int64_t 10 | from libc.string cimport memcpy 11 | from libcpp.unordered_map cimport unordered_map 12 | from libcpp.vector cimport vector as c_vector 13 | 14 | from _lightning_client cimport CLightningClient 15 | from cpython cimport Py_buffer, PyObject 16 | from cpython.buffer cimport PyBUF_SIMPLE, PyObject_CheckBuffer, PyBuffer_Release, PyObject_GetBuffer, PyBuffer_FillInfo, PyBUF_READ, PyBUF_WRITE 17 | from cpython.memoryview cimport PyMemoryView_FromMemory 18 | 19 | cdef class LightningStoreClient: 20 | cdef: 21 | unique_ptr[CLightningClient] client 22 | 23 | def __cinit__(self): 24 | pass 25 | 26 | def __init__(self, socket_name, password): 27 | cdef CLightningClient *new_client 28 | new_client = new CLightningClient(socket_name.encode(), password.encode()) 29 | self.client.reset(new_client) 30 | 31 | def put_buffer(self, obj, object_id): 32 | cdef: 33 | uint8_t *buf 34 | Py_buffer py_buf 35 | if not PyObject_CheckBuffer(obj): 36 | raise ValueError("Python object hasn't implemented the buffer interface") 37 | status = PyObject_GetBuffer(obj, &py_buf, PyBUF_SIMPLE) 38 | if status: 39 | raise ValueError("Failed to convert python object into buffer") 40 | status = self.client.get().Create(object_id, &buf, py_buf.len) 41 | if status == -1: 42 | # special case: the object exists 43 | return -1 44 | if status: 45 | raise Exception("Failed to create new object, error code = " + str(status)) 46 | memcpy(buf, py_buf.buf, py_buf.len) 47 | status = self.client.get().Seal(object_id) 48 | if status: 49 | raise Exception("Failed to seal new object, error code = " + str(status)) 50 | 51 | def get_buffer(self, object_id): 52 | cdef: 53 | uint8_t *ptr 54 | size_t size 55 | status = self.client.get().Get(object_id, &ptr, &size) 56 | if status: 57 | return None 58 | return PyMemoryView_FromMemory( ptr, size, PyBUF_WRITE) 59 | 60 | def release(self, object_id): 61 | status = self.client.get().Release(object_id) 62 | if status: 63 | raise Exception("Failed to release object {}, error code = {}".format(object_id, status)) 64 | 65 | def delete(self, object_id): 66 | status = self.client.get().Delete(object_id) 67 | if status: 68 | raise Exception("Failed to delete object {}, error code = {}".format(object_id, status)) 69 | -------------------------------------------------------------------------------- /python/setup.py: -------------------------------------------------------------------------------- 1 | import os 2 | from distutils.core import setup 3 | from distutils.extension import Extension 4 | from Cython.Build import cythonize 5 | 6 | ext_modules = [ 7 | Extension( 8 | "py_lightning_client", 9 | sources=["_lightning_client.pyx", "../src/log_disk.cc", "../src/object_log.cc", "../src/malloc.cc", "../src/client.cc"], 10 | include_dirs=[os.path.abspath("../inc/")], 11 | extra_compile_args=["-std=c++11"], 12 | extra_link_args=["-std=c++11"], 13 | ) 14 | ] 15 | 16 | setup(name="py_lightning_client", 17 | ext_modules=cythonize(ext_modules)) 18 | -------------------------------------------------------------------------------- /python/test_client.py: -------------------------------------------------------------------------------- 1 | import atexit 2 | import subprocess 3 | import time 4 | import numpy as np 5 | 6 | from py_lightning_client import LightningStoreClient 7 | 8 | proc = subprocess.Popen("../build/store") 9 | 10 | def cleanup(): 11 | global proc 12 | proc.terminate() 13 | 14 | def test(client, object_size): 15 | s = int(object_size/8) 16 | buf = np.random.randint(2 ** 30, size=s, dtype='l') 17 | print(buf.nbytes, end = ','), 18 | 19 | num_tests = 100 20 | 21 | start = time.time() 22 | for i in range(num_tests): 23 | client.put_buffer(buf, i) 24 | duration = time.time() - start 25 | print(duration/num_tests, end = ','), 26 | 27 | start = time.time() 28 | for i in range(num_tests): 29 | client.get_buffer(i) 30 | duration = time.time() - start 31 | print(duration/num_tests, end = ','), 32 | 33 | start = time.time() 34 | for i in range(num_tests): 35 | client.delete(i) 36 | duration = time.time() - start 37 | print(duration/num_tests) 38 | 39 | atexit.register(cleanup) 40 | time.sleep(1) 41 | 42 | client = LightningStoreClient("/tmp/lightning", "password") 43 | 44 | for i in range(0,100): 45 | object_size = 1024 * 1024 46 | while (object_size >= 16): 47 | test(client, object_size) 48 | object_size /=2 49 | -------------------------------------------------------------------------------- /script/recovery_latency.sh: -------------------------------------------------------------------------------- 1 | trap "trap - SIGTERM && kill -- -$$" SIGINT SIGTERM SIGHUP EXIT 2 | 3 | for i in `seq 0 4` 4 | do 5 | np=$[10**i] 6 | for j in `seq 0 9` 7 | do 8 | ./create_object $np 9 | sleep 2 10 | done 11 | done -------------------------------------------------------------------------------- /script/subscription_latency.sh: -------------------------------------------------------------------------------- 1 | trap "trap - SIGTERM && kill -- -$$" SIGINT SIGTERM SIGHUP EXIT 2 | 3 | mkdir -p results 4 | 5 | for waiter in 0 1 2 4 8 16 32 64 6 | do 7 | for index in `seq 0 9` 8 | do 9 | ./store & 10 | sleep 2 11 | for j in `seq 0 $[$waiter-1]` 12 | do 13 | ./subscribe & 14 | done 15 | sleep 10 16 | ./create_latency >results/waittest-$waiter-$index.txt 17 | 18 | sleep 2 19 | pkill store 20 | sleep 2 21 | done 22 | done -------------------------------------------------------------------------------- /script/throughput.sh: -------------------------------------------------------------------------------- 1 | trap "trap - SIGTERM && kill -- -$$" SIGINT SIGTERM SIGHUP EXIT 2 | 3 | mkdir -p results 4 | 5 | for i in `seq 0 4` 6 | do 7 | ./store & 8 | sleep 5 9 | 10 | np=$[2**i] 11 | for j in `seq $np` 12 | do 13 | ./mp_benchmark $j > results/lightning-$np-$j.txt & 14 | done 15 | 16 | sleep 20 17 | pkill mp_benchmark 18 | pkill store 19 | sleep 5 20 | done 21 | -------------------------------------------------------------------------------- /src/log_disk.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include "log_disk.h" 12 | 13 | UndoLogDisk::UndoLogDisk(size_t log_size, uint8_t *shm_base, size_t shm_size) 14 | : shm_base_(shm_base), shm_size_(shm_size) { 15 | pid_t pid = getpid(); 16 | auto pid_str = "log-" + std::to_string(pid); 17 | log_fd_ = shm_open(pid_str.c_str(), O_CREAT | O_RDWR, 0666); 18 | int status = ftruncate(log_fd_, log_size); 19 | if (status < 0) { 20 | perror("cannot ftruncate"); 21 | exit(-1); 22 | } 23 | 24 | log_base_ = 25 | (uint8_t *)mmap(nullptr, log_size, PROT_WRITE, MAP_SHARED, log_fd_, 0); 26 | } 27 | 28 | void UndoLogDisk::BeginTx() { 29 | *(uint64_t *)log_base_ = 0; 30 | std::atomic_thread_fence(std::memory_order_acquire); 31 | } 32 | 33 | void UndoLogDisk::CommitTx() { 34 | std::atomic_thread_fence(std::memory_order_release); 35 | *(uint64_t *)log_base_ = 0; 36 | } 37 | 38 | void UndoLogDisk::Write(sm_offset offset, uint64_t value) { 39 | assert(offset % 8 == 0); 40 | uint64_t num_entry = *(uint64_t *)log_base_; 41 | LogEntry *entry = (LogEntry *)(log_base_ + sizeof(uint64_t)); 42 | entry[num_entry].offset = offset; 43 | entry[num_entry].value = *(uint64_t *)&shm_base_[offset]; 44 | *(uint64_t *)log_base_ = num_entry + 1; 45 | std::atomic_thread_fence(std::memory_order_seq_cst); 46 | *(uint64_t *)&shm_base_[offset] = value; 47 | } 48 | -------------------------------------------------------------------------------- /src/malloc.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "malloc.h" 8 | 9 | MemAllocator::MemAllocator(LightningStoreHeader *header, UndoLogDisk *disk) 10 | : header_(header), disk_(disk) { 11 | base_ = (uint8_t *)header; 12 | free_list_ = &header->free_list; 13 | } 14 | 15 | static __always_inline int fls(unsigned long x) { 16 | int r = 64; 17 | if (!x) 18 | return 0; 19 | if (!(x & 0xffffffff00000000u)) { 20 | x <<= 32; 21 | r -= 32; 22 | } 23 | if (!(x & 0xffff000000000000u)) { 24 | x <<= 16; 25 | r -= 16; 26 | } 27 | if (!(x & 0xff00000000000000u)) { 28 | x <<= 8; 29 | r -= 8; 30 | } 31 | if (!(x & 0xf000000000000000u)) { 32 | x <<= 4; 33 | r -= 4; 34 | } 35 | if (!(x & 0xc000000000000000u)) { 36 | x <<= 2; 37 | r -= 2; 38 | } 39 | if (!(x & 0x8000000000000000u)) { 40 | x <<= 1; 41 | r -= 1; 42 | } 43 | return r; 44 | } 45 | 46 | int64_t MemAllocator::create_block(sm_offset start, size_t size) { 47 | int64_t mem_entry_index = allocate_memory_entry(); 48 | MemoryEntry *entry = &header_->memory_entries[mem_entry_index]; 49 | MemoryHeader *head = (MemoryHeader *)&base_[start]; 50 | 51 | LOGGED_WRITE(head->index, mem_entry_index, header_, disk_); 52 | // head->index = mem_entry_index; 53 | 54 | LOGGED_WRITE(entry->in_use, false, header_, disk_); 55 | // entry->in_use = false; 56 | 57 | LOGGED_WRITE(entry->offset, start, header_, disk_); 58 | // entry->offset = start; 59 | 60 | LOGGED_WRITE(entry->size, size, header_, disk_); 61 | // entry->size = size; 62 | 63 | LOGGED_WRITE(entry->prev, -1, header_, disk_); 64 | // entry->prev = -1; 65 | 66 | LOGGED_WRITE(entry->next, -1, header_, disk_); 67 | // entry->next = -1; 68 | 69 | for (int i = 0; i < 32; i++) { 70 | LOGGED_WRITE(entry->buddy[i], -1, header_, disk_); 71 | // entry->buddy[i] = -1; 72 | } 73 | 74 | return mem_entry_index; 75 | } 76 | 77 | void MemAllocator::add_to_free_list(int index, int64_t mem_entry_index) { 78 | assert(index >= MINIMAL_BLOCK_SIZE_LOG); 79 | assert(index <= MAXIMUM_BLOCK_SIZE_LOG); 80 | 81 | MemoryEntry *entry = &header_->memory_entries[mem_entry_index]; 82 | 83 | assert(!entry->in_use); 84 | assert(1 << index == entry->size); 85 | 86 | if (free_list_->free_list_head[index] > 0) { 87 | int64_t head_index = free_list_->free_list_head[index]; 88 | MemoryEntry *head = &header_->memory_entries[head_index]; 89 | LOGGED_WRITE(head->prev, mem_entry_index, header_, disk_); 90 | // head->prev = mem_entry_index; 91 | 92 | LOGGED_WRITE(entry->next, head_index, header_, disk_); 93 | // entry->next = head_index; 94 | } 95 | 96 | LOGGED_WRITE(free_list_->free_list_head[index], mem_entry_index, header_, 97 | disk_); 98 | // free_list_->free_list_head[index] = mem_entry_index; 99 | } 100 | 101 | int64_t MemAllocator::allocate_memory_entry() { 102 | int64_t i = header_->memory_entry_free_list; 103 | assert(i >= 0); 104 | 105 | LOGGED_WRITE(header_->memory_entry_free_list, 106 | header_->memory_entries[i].free_list_next, header_, disk_); 107 | // header_->memory_entry_free_list = 108 | // header_->memory_entries[i].free_list_next; 109 | 110 | LOGGED_WRITE(header_->memory_entries[i].free_list_next, -1, header_, disk_); 111 | // header_->memory_entries[i].free_list_next = -1; 112 | return i; 113 | } 114 | 115 | int64_t MemAllocator::allocate_memory_entry_nolog() { 116 | int64_t i = header_->memory_entry_free_list; 117 | assert(i >= 0); 118 | 119 | header_->memory_entry_free_list = header_->memory_entries[i].free_list_next; 120 | header_->memory_entries[i].free_list_next = -1; 121 | return i; 122 | } 123 | 124 | void MemAllocator::deallocate_memory_entry(int64_t i) { 125 | assert(header_->memory_entries[i].free_list_next == -1); 126 | int64_t j = header_->memory_entry_free_list; 127 | LOGGED_WRITE(header_->memory_entries[i].free_list_next, j, header_, disk_); 128 | // header_->memory_entries[i].free_list_next = j; 129 | LOGGED_WRITE(header_->memory_entry_free_list, i, header_, disk_); 130 | // header_->memory_entry_free_list = i; 131 | } 132 | 133 | void MemAllocator::deallocate_memory_entry_nolog(int64_t i) { 134 | assert(header_->memory_entries[i].free_list_next == -1); 135 | int64_t j = header_->memory_entry_free_list; 136 | header_->memory_entries[i].free_list_next = j; 137 | header_->memory_entry_free_list = i; 138 | } 139 | 140 | int64_t MemAllocator::create_block_nolog(sm_offset start, size_t size) { 141 | int64_t mem_entry_index = allocate_memory_entry_nolog(); 142 | MemoryEntry *entry = &header_->memory_entries[mem_entry_index]; 143 | MemoryHeader *head = (MemoryHeader *)&base_[start]; 144 | head->index = mem_entry_index; 145 | 146 | entry->in_use = false; 147 | entry->offset = start; 148 | entry->size = size; 149 | entry->prev = -1; 150 | entry->next = -1; 151 | for (int i = 0; i < 32; i++) { 152 | entry->buddy[i] = -1; 153 | } 154 | 155 | return mem_entry_index; 156 | } 157 | 158 | void MemAllocator::add_to_free_list_nolog(int index, int64_t mem_entry_index) { 159 | assert(index >= MINIMAL_BLOCK_SIZE_LOG); 160 | assert(index <= MAXIMUM_BLOCK_SIZE_LOG); 161 | 162 | MemoryEntry *entry = &header_->memory_entries[mem_entry_index]; 163 | 164 | assert(!entry->in_use); 165 | assert(1 << index == entry->size); 166 | 167 | if (free_list_->free_list_head[index] > 0) { 168 | int64_t head_index = free_list_->free_list_head[index]; 169 | MemoryEntry *head = &header_->memory_entries[head_index]; 170 | head->prev = mem_entry_index; 171 | entry->next = head_index; 172 | } 173 | 174 | free_list_->free_list_head[index] = mem_entry_index; 175 | } 176 | 177 | void MemAllocator::split_memory_to_free_lists(sm_offset offset, size_t size) { 178 | sm_offset cur_offset = offset; 179 | size_t cur_size = size; 180 | while (true) { 181 | int size_index = MINIMAL_BLOCK_SIZE_LOG; 182 | size_t block_size = 1 << size_index; 183 | 184 | if (cur_size < block_size) { 185 | return; 186 | } 187 | 188 | while (cur_size > block_size * 2) { 189 | size_index++; 190 | block_size *= 2; 191 | } 192 | 193 | int64_t cur_index = create_block_nolog(cur_offset, block_size); 194 | add_to_free_list_nolog(size_index, cur_index); 195 | 196 | cur_offset += block_size; 197 | cur_size -= block_size; 198 | } 199 | } 200 | 201 | void MemAllocator::Init(sm_offset offset, size_t size) { 202 | // initialize free lists 203 | for (int i = 0; i <= 32; i++) { 204 | free_list_->free_list_head[i] = -1; 205 | } 206 | 207 | split_memory_to_free_lists(offset, size); 208 | } 209 | 210 | int64_t MemAllocator::remove_from_free_list(int index) { 211 | assert(index >= MINIMAL_BLOCK_SIZE_LOG); 212 | assert(index <= MAXIMUM_BLOCK_SIZE_LOG); 213 | 214 | // test if the free list is vacant 215 | assert(free_list_->free_list_head[index] >= 0); 216 | 217 | int64_t head_index = free_list_->free_list_head[index]; 218 | MemoryEntry *head = &header_->memory_entries[head_index]; 219 | LOGGED_WRITE(free_list_->free_list_head[index], head->next, header_, disk_); 220 | // free_list_->free_list_head[index] = head->next; 221 | 222 | if (free_list_->free_list_head[index] >= 0) { 223 | int64_t new_head_index = free_list_->free_list_head[index]; 224 | MemoryEntry *new_head = &header_->memory_entries[new_head_index]; 225 | LOGGED_WRITE(new_head->prev, -1, header_, disk_); 226 | // new_head->prev = -1; 227 | } 228 | LOGGED_WRITE(head->next, -1, header_, disk_); 229 | // head->next = -1; 230 | 231 | // if the removed block is already in use, there is a huge problem 232 | assert(!head->in_use); 233 | return head_index; 234 | } 235 | 236 | int64_t MemAllocator::separate_buddy(int64_t mem_index, int index) { 237 | MemoryEntry *entry = &header_->memory_entries[mem_index]; 238 | size_t block_size = (entry->size) >> 1; 239 | 240 | assert(!entry->in_use); 241 | 242 | sm_offset smaller_block_offset = entry->offset + block_size; 243 | int64_t smaller_index = create_block(smaller_block_offset, block_size); 244 | 245 | assert(smaller_index != mem_index); 246 | 247 | LOGGED_WRITE(entry->buddy[index], smaller_index, header_, disk_); 248 | // entry->buddy[index] = smaller_index; 249 | 250 | MemoryEntry *smaller_entry = &header_->memory_entries[smaller_index]; 251 | 252 | LOGGED_WRITE(smaller_entry->buddy[index], mem_index, header_, disk_); 253 | // smaller_entry->buddy[index] = mem_index; 254 | 255 | LOGGED_WRITE(entry->size, block_size, header_, disk_); 256 | // entry->size = block_size; 257 | 258 | return smaller_index; 259 | } 260 | 261 | int64_t MemAllocator::get_free_block(int size_index) { 262 | if (free_list_->free_list_head[size_index] >= 0) { 263 | return remove_from_free_list(size_index); 264 | } 265 | 266 | // find a block that is larger than the request block 267 | int i = size_index + 1; 268 | while (free_list_->free_list_head[i] < 0) { 269 | assert(i <= MAXIMUM_BLOCK_SIZE_LOG); 270 | i++; 271 | } 272 | 273 | int64_t mem_entry_index = remove_from_free_list(i); 274 | 275 | // need to break the block (i - index) times 276 | for (int j = i - 1; j >= size_index; j--) { 277 | int64_t smaller_block_index = separate_buddy(mem_entry_index, j); 278 | add_to_free_list(j, smaller_block_index); 279 | } 280 | 281 | return mem_entry_index; 282 | } 283 | 284 | sm_offset MemAllocator::MallocShared(size_t size) { 285 | size_t real_size = size + sizeof(MemoryEntry); 286 | 287 | int size_index = fls(real_size - 1); 288 | if (size_index < MINIMAL_BLOCK_SIZE_LOG) { 289 | size_index = MINIMAL_BLOCK_SIZE_LOG; 290 | } 291 | 292 | int64_t mem_index = get_free_block(size_index); 293 | assert(mem_index >= 0); 294 | 295 | MemoryEntry *entry = &header_->memory_entries[mem_index]; 296 | LOGGED_WRITE(entry->in_use, true, header_, disk_); 297 | // entry->in_use = true; 298 | 299 | return entry->offset + sizeof(MemoryHeader); 300 | } 301 | 302 | void MemAllocator::remove_block(int index, int64_t mem_entry_index) { 303 | assert(index >= MINIMAL_BLOCK_SIZE_LOG); 304 | assert(index <= MAXIMUM_BLOCK_SIZE_LOG); 305 | 306 | assert(free_list_->free_list_head[index] >= 0); 307 | 308 | int64_t head_index = free_list_->free_list_head[index]; 309 | MemoryEntry *head = &header_->memory_entries[head_index]; 310 | MemoryEntry *block = &header_->memory_entries[mem_entry_index]; 311 | if (head_index == mem_entry_index) { 312 | LOGGED_WRITE(free_list_->free_list_head[index], head->next, header_, disk_); 313 | // free_list_->free_list_head[index] = head->next; 314 | if (free_list_->free_list_head[index] >= 0) { 315 | int64_t new_head_index = free_list_->free_list_head[index]; 316 | MemoryEntry *new_head = &header_->memory_entries[new_head_index]; 317 | LOGGED_WRITE(new_head->prev, -1, header_, disk_); 318 | // new_head->prev = -1; 319 | } 320 | LOGGED_WRITE(head->next, -1, header_, disk_); 321 | // head->next = -1; 322 | } else { 323 | int64_t cur_index = head_index; 324 | MemoryEntry *cur = head; 325 | while (cur->next >= 0) { 326 | if (cur->next == mem_entry_index) { 327 | LOGGED_WRITE(cur->next, block->next, header_, disk_); 328 | // cur->next = block->next; 329 | if (block->next >= 0) { 330 | MemoryEntry *next_block = &header_->memory_entries[cur->next]; 331 | LOGGED_WRITE(next_block->prev, cur_index, header_, disk_); 332 | // next_block->prev = cur_index; 333 | } 334 | LOGGED_WRITE(block->next, -1, header_, disk_); 335 | // block->next = -1; 336 | 337 | LOGGED_WRITE(block->prev, -1, header_, disk_); 338 | // block->prev = -1; 339 | return; 340 | } 341 | cur_index = cur->next; 342 | cur = &header_->memory_entries[cur_index]; 343 | } 344 | assert(false); 345 | } 346 | } 347 | 348 | void MemAllocator::remove_block_nolog(int index, int64_t mem_entry_index) { 349 | assert(index >= MINIMAL_BLOCK_SIZE_LOG); 350 | assert(index <= MAXIMUM_BLOCK_SIZE_LOG); 351 | 352 | assert(free_list_->free_list_head[index] >= 0); 353 | 354 | int64_t head_index = free_list_->free_list_head[index]; 355 | MemoryEntry *head = &header_->memory_entries[head_index]; 356 | MemoryEntry *block = &header_->memory_entries[mem_entry_index]; 357 | if (head_index == mem_entry_index) { 358 | free_list_->free_list_head[index] = head->next; 359 | if (free_list_->free_list_head[index] >= 0) { 360 | int64_t new_head_index = free_list_->free_list_head[index]; 361 | MemoryEntry *new_head = &header_->memory_entries[new_head_index]; 362 | new_head->prev = -1; 363 | } 364 | head->next = -1; 365 | } else { 366 | int64_t cur_index = head_index; 367 | MemoryEntry *cur = head; 368 | while (cur->next >= 0) { 369 | if (cur->next == mem_entry_index) { 370 | cur->next = block->next; 371 | if (block->next >= 0) { 372 | MemoryEntry *next_block = &header_->memory_entries[cur->next]; 373 | next_block->prev = cur_index; 374 | } 375 | block->next = -1; 376 | block->prev = -1; 377 | return; 378 | } 379 | cur_index = cur->next; 380 | cur = &header_->memory_entries[cur_index]; 381 | } 382 | assert(false); 383 | } 384 | } 385 | 386 | int64_t MemAllocator::merge_blocks(int64_t block1_entry_index, 387 | int64_t block2_entry_index, int index) { 388 | 389 | MemoryEntry *block1 = &header_->memory_entries[block1_entry_index]; 390 | MemoryEntry *block2 = &header_->memory_entries[block2_entry_index]; 391 | 392 | assert(block1->buddy[index] == block2_entry_index); 393 | assert(block2->buddy[index] == block1_entry_index); 394 | 395 | remove_block(index, block2_entry_index); 396 | 397 | int64_t first, second; 398 | if (block1->offset < block2->offset) { 399 | first = block1_entry_index; 400 | second = block2_entry_index; 401 | } else { 402 | first = block2_entry_index; 403 | second = block1_entry_index; 404 | } 405 | 406 | MemoryEntry *first_block = &header_->memory_entries[first]; 407 | MemoryEntry *second_block = &header_->memory_entries[second]; 408 | 409 | LOGGED_WRITE(first_block->buddy[index], -1, header_, disk_); 410 | // first_block->buddy[index] = -1; 411 | 412 | LOGGED_WRITE(second_block->buddy[index], -1, header_, disk_); 413 | // second_block->buddy[index] = -1; 414 | 415 | LOGGED_WRITE(first_block->size, first_block->size + second_block->size, 416 | header_, disk_); 417 | // first_block->size = first_block->size + second_block->size; 418 | 419 | MemoryHeader *header = (MemoryHeader *)&base_[first_block->offset]; 420 | 421 | LOGGED_WRITE(header->index, first, header_, disk_); 422 | // header->index = first; 423 | 424 | deallocate_memory_entry(second); 425 | 426 | return first; 427 | } 428 | 429 | int64_t MemAllocator::merge_blocks_nolog(int64_t block1_entry_index, 430 | int64_t block2_entry_index, 431 | int index) { 432 | 433 | MemoryEntry *block1 = &header_->memory_entries[block1_entry_index]; 434 | MemoryEntry *block2 = &header_->memory_entries[block2_entry_index]; 435 | 436 | assert(block1->buddy[index] == block2_entry_index); 437 | assert(block2->buddy[index] == block1_entry_index); 438 | 439 | remove_block_nolog(index, block2_entry_index); 440 | 441 | int64_t first, second; 442 | if (block1->offset < block2->offset) { 443 | first = block1_entry_index; 444 | second = block2_entry_index; 445 | } else { 446 | first = block2_entry_index; 447 | second = block1_entry_index; 448 | } 449 | 450 | MemoryEntry *first_block = &header_->memory_entries[first]; 451 | MemoryEntry *second_block = &header_->memory_entries[second]; 452 | 453 | first_block->buddy[index] = -1; 454 | second_block->buddy[index] = -1; 455 | first_block->size = first_block->size + second_block->size; 456 | 457 | MemoryHeader *header = (MemoryHeader *)&base_[first_block->offset]; 458 | header->index = first; 459 | deallocate_memory_entry_nolog(second); 460 | 461 | return first; 462 | } 463 | 464 | int fls_uninlined(size_t size) { return fls(size); } 465 | 466 | void MemAllocator::FreeShared(sm_offset offset) { 467 | sm_offset block_offset = offset - sizeof(MemoryHeader); 468 | MemoryHeader *header = (MemoryHeader *)&base_[block_offset]; 469 | 470 | int64_t mem_entry_index = header->index; 471 | MemoryEntry *entry = &header_->memory_entries[mem_entry_index]; 472 | /* 473 | if (entry->offset != block_offset) { 474 | std::cout << entry->offset << " != " << block_offset << std::endl; 475 | } 476 | */ 477 | assert(entry->offset == block_offset); 478 | assert(entry->in_use); 479 | 480 | size_t size = entry->size; 481 | 482 | int index = fls_uninlined(size - 1); 483 | 484 | LOGGED_WRITE(entry->in_use, false, header_, disk_); 485 | // entry->in_use = false; 486 | 487 | for (int i = index; i < MAXIMUM_BLOCK_SIZE_LOG; i++) { 488 | int64_t buddy_mem_entry_index = entry->buddy[i]; 489 | 490 | MemoryEntry *buddy_entry = &header_->memory_entries[buddy_mem_entry_index]; 491 | 492 | if (buddy_mem_entry_index < 0 || buddy_entry->in_use || 493 | buddy_entry->size != entry->size) { 494 | add_to_free_list(i, mem_entry_index); 495 | return; 496 | } 497 | assert(mem_entry_index != buddy_mem_entry_index); 498 | mem_entry_index = merge_blocks(mem_entry_index, buddy_mem_entry_index, i); 499 | entry = &header_->memory_entries[mem_entry_index]; 500 | } 501 | 502 | add_to_free_list(MAXIMUM_BLOCK_SIZE_LOG, mem_entry_index); 503 | } 504 | 505 | void MemAllocator::PrintAvalaibleMemory() { 506 | for (int i = 0; i < 32; i++) { 507 | if (free_list_->free_list_head[i] > 0) { 508 | 509 | int64_t mem_entry_index = free_list_->free_list_head[i]; 510 | std::cout << i << ":"; 511 | 512 | while (mem_entry_index >= 0) { 513 | MemoryEntry *cur = 514 | (MemoryEntry *)&header_->memory_entries[mem_entry_index]; 515 | std::cout << " " << mem_entry_index << ","; 516 | mem_entry_index = cur->next; 517 | } 518 | std::cout << std::endl; 519 | } else { 520 | std::cout << i << ":" << std::endl; 521 | } 522 | } 523 | } 524 | 525 | void MemAllocator::FreeSharedNoLog(sm_offset offset) { 526 | sm_offset block_offset = offset - sizeof(MemoryHeader); 527 | MemoryHeader *header = (MemoryHeader *)&base_[block_offset]; 528 | 529 | int64_t mem_entry_index = header->index; 530 | MemoryEntry *entry = &header_->memory_entries[mem_entry_index]; 531 | assert(entry->offset == block_offset); 532 | 533 | size_t size = entry->size; 534 | 535 | int index = fls_uninlined(size - 1); 536 | entry->in_use = false; 537 | 538 | for (int i = index; i < MAXIMUM_BLOCK_SIZE_LOG; i++) { 539 | int64_t buddy_mem_entry_index = entry->buddy[i]; 540 | 541 | MemoryEntry *buddy_entry = &header_->memory_entries[buddy_mem_entry_index]; 542 | 543 | if (buddy_mem_entry_index < 0 || buddy_entry->in_use || 544 | buddy_entry->size != entry->size) { 545 | add_to_free_list_nolog(i, mem_entry_index); 546 | return; 547 | } 548 | 549 | assert(mem_entry_index != buddy_mem_entry_index); 550 | mem_entry_index = 551 | merge_blocks_nolog(mem_entry_index, buddy_mem_entry_index, i); 552 | entry = &header_->memory_entries[mem_entry_index]; 553 | } 554 | 555 | add_to_free_list_nolog(MAXIMUM_BLOCK_SIZE_LOG, mem_entry_index); 556 | } 557 | -------------------------------------------------------------------------------- /src/object_log.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include "object_log.h" 12 | 13 | ObjectLog::ObjectLog(uint8_t *object_log_base, sm_offset object_log_offset, 14 | UndoLogDisk *disk) 15 | : object_log_base_(object_log_base), object_log_offset_(object_log_offset), 16 | disk_(disk) { 17 | LogObjectEntry *objects = (LogObjectEntry *)&object_log_base_[0]; 18 | for (int i = 0; i < OBJECT_LOG_SIZE; i++) { 19 | objects[i].in_use = false; 20 | } 21 | } 22 | 23 | int64_t ObjectLog::find_object(int64_t object_id) { 24 | auto search = object_cache_.find(object_id); 25 | if (search == object_cache_.end()) { 26 | return -1; 27 | } 28 | return object_cache_[object_id]; 29 | } 30 | 31 | void ObjectLog::erase_object(int64_t object_id) { 32 | auto search = object_cache_.find(object_id); 33 | if (search == object_cache_.end()) { 34 | return; 35 | } 36 | object_cache_.erase(search); 37 | } 38 | 39 | void ObjectLog::insert_object(int64_t object_id, int64_t index) { 40 | object_cache_[object_id] = index; 41 | } 42 | 43 | int64_t ObjectLog::find_new_entry(int64_t object_id) { 44 | LogObjectEntry *objects = (LogObjectEntry *)&object_log_base_[0]; 45 | int index = object_id % OBJECT_LOG_SIZE; 46 | if (index < 0) { 47 | index += OBJECT_LOG_SIZE; 48 | } 49 | while (true) { 50 | if (!objects[index].in_use) { 51 | return index; 52 | } 53 | index++; 54 | if (index >= OBJECT_LOG_SIZE) 55 | index -= OBJECT_LOG_SIZE; 56 | } 57 | } 58 | 59 | void ObjectLog::OpenObject(int64_t object_id) { 60 | if (find_object(object_id) >= 0) { 61 | return; 62 | } 63 | 64 | int64_t index = find_new_entry(object_id); 65 | 66 | assert(index >= 0); 67 | assert(index < OBJECT_LOG_SIZE); 68 | disk_->Write(object_log_offset_ + index * sizeof(LogObjectEntry), object_id); 69 | disk_->Write(object_log_offset_ + index * sizeof(LogObjectEntry) + 8, true); 70 | 71 | insert_object(object_id, index); 72 | } 73 | 74 | void ObjectLog::CloseObject(int64_t object_id) { 75 | int64_t index = find_object(object_id); 76 | if (index < 0) { 77 | return; 78 | } 79 | 80 | assert(index >= 0); 81 | assert(index < OBJECT_LOG_SIZE); 82 | 83 | disk_->Write(object_log_offset_ + index * sizeof(LogObjectEntry) + 8, false); 84 | erase_object(object_id); 85 | } -------------------------------------------------------------------------------- /src/store.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | #include "log_disk.h" 20 | #include "object_log.h" 21 | #include "store.h" 22 | 23 | const char *name = "lightning"; 24 | 25 | void signal_handler(int sig_number) { 26 | std::cout << "Capture Ctrl+C" << std::endl; 27 | exit(0); 28 | } 29 | 30 | int send_fd(int unix_sock, int fd) { 31 | ssize_t size; 32 | struct msghdr msg; 33 | struct iovec iov; 34 | union { 35 | struct cmsghdr cmsghdr; 36 | char control[CMSG_SPACE(sizeof(int))]; 37 | } cmsgu; 38 | struct cmsghdr *cmsg; 39 | char buf[2]; 40 | 41 | iov.iov_base = buf; 42 | iov.iov_len = 2; 43 | 44 | msg.msg_name = NULL; 45 | msg.msg_namelen = 0; 46 | msg.msg_iov = &iov; 47 | msg.msg_iovlen = 1; 48 | 49 | if (fd != -1) { 50 | msg.msg_control = cmsgu.control; 51 | msg.msg_controllen = sizeof(cmsgu.control); 52 | 53 | cmsg = CMSG_FIRSTHDR(&msg); 54 | cmsg->cmsg_len = CMSG_LEN(sizeof(int)); 55 | cmsg->cmsg_level = SOL_SOCKET; 56 | cmsg->cmsg_type = SCM_RIGHTS; 57 | 58 | int *fd_p = (int *)CMSG_DATA(cmsg); 59 | *fd_p = fd; 60 | } else { 61 | msg.msg_control = NULL; 62 | msg.msg_controllen = 0; 63 | } 64 | 65 | size = sendmsg(unix_sock, &msg, 0); 66 | 67 | if (size < 0) { 68 | std::cerr << "recvmsg error" << std::endl; 69 | } 70 | return size; 71 | } 72 | 73 | LightningStore::LightningStore(const std::string &unix_socket, int size) 74 | : unix_socket_(unix_socket), size_(size) { 75 | store_fd_ = shm_open(name, O_CREAT | O_RDWR, 0666); 76 | int status = ftruncate(store_fd_, size); 77 | if (status < 0) { 78 | perror("cannot ftruncate"); 79 | exit(-1); 80 | } 81 | store_header_ = 82 | (LightningStoreHeader *)mmap((void *)0xabcd000, size, PROT_WRITE, 83 | MAP_SHARED | MAP_FIXED, store_fd_, 0); 84 | if (store_header_ == (LightningStoreHeader *)-1) { 85 | perror("mmap failed"); 86 | exit(-1); 87 | } 88 | 89 | shm_unlink(name); 90 | 91 | std::cout << "store_header_ = " << (unsigned long)store_header_ << std::endl; 92 | 93 | store_header_ = new (store_header_) LightningStoreHeader; 94 | 95 | for (int i = 0; i < MAX_NUM_OBJECTS - 1; i++) { 96 | store_header_->memory_entries[i].free_list_next = i + 1; 97 | } 98 | store_header_->memory_entries[MAX_NUM_OBJECTS - 1].free_list_next = -1; 99 | 100 | for (int i = 0; i < MAX_NUM_OBJECTS - 1; i++) { 101 | store_header_->object_entries[i].free_list_next = i + 1; 102 | } 103 | store_header_->object_entries[MAX_NUM_OBJECTS - 1].free_list_next = -1; 104 | 105 | allocator_ = new MemAllocator((LightningStoreHeader *)store_header_, nullptr); 106 | 107 | int num_mpk_pages = sizeof(LightningStoreHeader) / 4096 + 1; 108 | 109 | int64_t secure_memory_size = num_mpk_pages * 4096; 110 | 111 | allocator_->Init(secure_memory_size, size - secure_memory_size); 112 | 113 | for (int i = 0; i < HASHMAP_SIZE; i++) { 114 | store_header_->hashmap.hash_entries[i].object_list = -1; 115 | } 116 | } 117 | 118 | int64_t LightningStore::find_object(uint64_t object_id) { 119 | int64_t head_index = 120 | store_header_->hashmap.hash_entries[object_id % 65536].object_list; 121 | int64_t current_index = head_index; 122 | 123 | while (current_index >= 0) { 124 | ObjectEntry *current = &store_header_->object_entries[current_index]; 125 | if (current->object_id == object_id) { 126 | return current_index; 127 | } 128 | current_index = current->next; 129 | } 130 | 131 | return -1; 132 | } 133 | 134 | int LightningStore::release_object(uint64_t object_id) { 135 | uint8_t *base = (uint8_t *)store_header_; 136 | 137 | int64_t object_index = find_object(object_id); 138 | assert(object_index >= 0); 139 | 140 | ObjectEntry *object_entry = &store_header_->object_entries[object_index]; 141 | object_entry->ref_count--; 142 | if (object_entry->ref_count == 0) { 143 | allocator_->FreeSharedNoLog(object_entry->offset); 144 | int64_t prev_object_index = object_entry->prev; 145 | int64_t next_object_index = object_entry->next; 146 | 147 | if (prev_object_index < 0) { 148 | if (next_object_index >= 0) { 149 | ObjectEntry *next = &store_header_->object_entries[next_object_index]; 150 | next->prev = -1; 151 | } 152 | store_header_->hashmap.hash_entries[object_id % 65536].object_list = 153 | next_object_index; 154 | } else { 155 | ObjectEntry *prev = &store_header_->object_entries[prev_object_index]; 156 | prev->next = next_object_index; 157 | if (next_object_index >= 0) { 158 | ObjectEntry *next = &store_header_->object_entries[next_object_index]; 159 | next->prev = prev_object_index; 160 | } 161 | } 162 | 163 | int64_t j = store_header_->object_entry_free_list; 164 | store_header_->object_entries[object_index].free_list_next = j; 165 | store_header_->object_entry_free_list = object_index; 166 | } 167 | return 0; 168 | } 169 | 170 | void LightningStore::recover(uint8_t *base, uint8_t *log, uint8_t *object_log, 171 | pid_t pid) { 172 | int hashmap_size = sizeof(LogObjectEntry) * OBJECT_LOG_SIZE; 173 | if (store_header_->lock_flag == pid) { 174 | std::cout << "undo log will be replayed!" << std::endl; 175 | uint64_t log_length = *(uint64_t *)log; 176 | 177 | if (log_length > 0) { 178 | for (uint64_t i = log_length - 1; i >= 0; i--) { 179 | uint64_t offset = i * sizeof(LogEntry) + sizeof(uint64_t); 180 | LogEntry *entry = (LogEntry *)&log[offset]; 181 | uint64_t *ptr = (uint64_t *)&base[entry->offset]; 182 | *ptr = entry->value; 183 | if (i == 0) { 184 | break; 185 | } 186 | } 187 | } 188 | } else { 189 | while (!__sync_bool_compare_and_swap(&store_header_->lock_flag, 0, 1)) { 190 | nanosleep((const struct timespec[]){{0, 0L}}, NULL); 191 | } 192 | } 193 | 194 | // garbage collect open objects 195 | LogObjectEntry *objects = (LogObjectEntry *)object_log; 196 | for (int i = 0; i < OBJECT_LOG_SIZE; i++) { 197 | if (objects[i].in_use) { 198 | // std::cout << "releasing object " << objects[i].object_id << std::endl; 199 | release_object(objects[i].object_id); 200 | } 201 | } 202 | 203 | std::atomic_thread_fence(std::memory_order_release); 204 | store_header_->lock_flag = 0; 205 | } 206 | 207 | bool is_number(char *str) { 208 | while (*str != '\0') { 209 | if (!std::isdigit(*str)) { 210 | return false; 211 | } 212 | str++; 213 | } 214 | return true; 215 | } 216 | 217 | void get_processes(std::unordered_set *processes) { 218 | DIR *dp = opendir("/proc"); 219 | 220 | if (dp == nullptr) { 221 | std::cerr << "cannot access procfs!" << std::endl; 222 | } 223 | 224 | struct dirent *dirp = readdir(dp); 225 | while (dirp != nullptr) { 226 | if (is_number(dirp->d_name)) { 227 | processes->insert(std::atoi(dirp->d_name)); 228 | } 229 | 230 | dirp = readdir(dp); 231 | } 232 | 233 | closedir(dp); 234 | } 235 | 236 | void LightningStore::monitor() { 237 | while (true) { 238 | { 239 | // scan the set of clients to find crashed ones 240 | std::lock_guard guard(client_lock_); 241 | std::unordered_set processes; 242 | get_processes(&processes); 243 | 244 | for (pid_t pid : clients_) { 245 | auto got = processes.find(pid); 246 | if (got == processes.end()) { 247 | std::cout << "pid = " << pid << " crashes! start recovering!" 248 | << std::endl; 249 | // map object log into memory 250 | 251 | auto object_log_name = "object-log-" + std::to_string(pid); 252 | int object_log_fd = 253 | shm_open(object_log_name.c_str(), O_CREAT | O_RDWR, 0666); 254 | int object_log_size = sizeof(LogObjectEntry) * OBJECT_LOG_SIZE; 255 | int status = ftruncate(object_log_fd, object_log_size); 256 | uint8_t *base = (uint8_t *)store_header_; 257 | uint8_t *object_log_base = base + size_; 258 | object_log_base = 259 | (uint8_t *)mmap(object_log_base, object_log_size, PROT_WRITE, 260 | MAP_SHARED | MAP_FIXED, object_log_fd, 0); 261 | if (object_log_base != base + size_) { 262 | perror("mmap failure"); 263 | exit(-1); 264 | } 265 | 266 | // map client log into memory 267 | auto name = "log-" + std::to_string(pid); 268 | int log_fd = shm_open(name.c_str(), O_CREAT | O_RDWR, 0666); 269 | // log is 1MB large 270 | int size = 1024 * 1024 * 10; 271 | status = ftruncate(log_fd, size); 272 | uint8_t *log_base = 273 | (uint8_t *)mmap(nullptr, size, PROT_WRITE, MAP_SHARED, log_fd, 0); 274 | auto start = std::chrono::high_resolution_clock::now(); 275 | 276 | recover((uint8_t *)store_header_, log_base, object_log_base, pid); 277 | munmap(log_base, size); 278 | close(log_fd); 279 | shm_unlink(name.c_str()); 280 | munmap(object_log_base, object_log_size); 281 | close(object_log_fd); 282 | shm_unlink(object_log_name.c_str()); 283 | 284 | clients_.erase(pid); 285 | auto end = std::chrono::high_resolution_clock::now(); 286 | std::chrono::duration duration = end - start; 287 | 288 | std::cout << "recovered using " << duration.count() << " s!" 289 | << std::endl; 290 | } 291 | } 292 | } 293 | usleep(1000000); 294 | } 295 | } 296 | 297 | int64_t LightningStore::alloc_object_entry() { 298 | int64_t i = store_header_->object_entry_free_list; 299 | store_header_->object_entry_free_list = 300 | store_header_->object_entries[i].free_list_next; 301 | store_header_->object_entries[i].free_list_next = -1; 302 | return i; 303 | } 304 | 305 | void LightningStore::dealloc_object_entry(int64_t i) { 306 | int64_t j = store_header_->object_entry_free_list; 307 | store_header_->object_entries[i].free_list_next = j; 308 | store_header_->object_entry_free_list = i; 309 | } 310 | 311 | int LightningStore::create_object(uint64_t object_id, sm_offset *offset_ptr, 312 | size_t size) { 313 | int64_t object_index = find_object(object_id); 314 | 315 | if (object_index >= 0) { 316 | ObjectEntry *object = &store_header_->object_entries[object_index]; 317 | if (object->offset > 0) { 318 | // object is already created 319 | return -1; 320 | } 321 | sm_offset object_buffer_offset = allocator_->MallocShared(size); 322 | 323 | object->offset = object_buffer_offset; 324 | object->size = size; 325 | object->ref_count = 1; 326 | *offset_ptr = object_buffer_offset; 327 | 328 | return 0; 329 | } 330 | 331 | int64_t new_object_index = alloc_object_entry(); 332 | sm_offset object_buffer_offset = allocator_->MallocShared(size); 333 | ObjectEntry *new_object = &store_header_->object_entries[new_object_index]; 334 | // uint8_t *object_buffer = &base_[object_buffer_offset]; 335 | 336 | new_object->object_id = object_id; 337 | new_object->num_waiters = 0; 338 | new_object->offset = object_buffer_offset; 339 | new_object->size = size; 340 | new_object->ref_count = 1; 341 | new_object->sealed = false; 342 | 343 | int64_t head_index = 344 | store_header_->hashmap.hash_entries[object_id % 65536].object_list; 345 | ObjectEntry *head = &store_header_->object_entries[head_index]; 346 | 347 | new_object->next = head_index; 348 | new_object->prev = -1; 349 | 350 | if (head_index >= 0) { 351 | head->prev = new_object_index; 352 | } 353 | store_header_->hashmap.hash_entries[object_id % 65536].object_list = 354 | new_object_index; 355 | 356 | *offset_ptr = object_buffer_offset; 357 | return 0; 358 | } 359 | 360 | int LightningStore::seal_object(uint64_t object_id) { 361 | int64_t object_index = find_object(object_id); 362 | assert(object_index >= 0); 363 | 364 | ObjectEntry *object_entry = &store_header_->object_entries[object_index]; 365 | object_entry->sealed = true; 366 | return 0; 367 | } 368 | 369 | int LightningStore::get_object(uint64_t object_id, sm_offset *ptr, 370 | size_t *size) { 371 | int64_t object_index = find_object(object_id); 372 | if (object_index < 0) { 373 | // object not found 374 | return -1; 375 | } 376 | ObjectEntry *object_entry = &store_header_->object_entries[object_index]; 377 | 378 | if (!object_entry->sealed) { 379 | // object is not sealed yet 380 | return -1; 381 | } 382 | *ptr = object_entry->offset; 383 | *size = object_entry->size; 384 | object_entry->ref_count++; 385 | 386 | return 0; 387 | } 388 | 389 | int LightningStore::delete_object(uint64_t object_id) { 390 | int64_t object_index = find_object(object_id); 391 | assert(object_index >= 0); 392 | 393 | ObjectEntry *object_entry = &store_header_->object_entries[object_index]; 394 | assert(object_entry->sealed); 395 | allocator_->FreeShared(object_entry->offset); 396 | int64_t prev_object_index = object_entry->prev; 397 | int64_t next_object_index = object_entry->next; 398 | 399 | if (prev_object_index < 0) { 400 | if (next_object_index > 0) { 401 | ObjectEntry *next = &store_header_->object_entries[next_object_index]; 402 | next->prev = -1; 403 | } 404 | store_header_->hashmap.hash_entries[object_id % 65536].object_list = 405 | next_object_index; 406 | } else { 407 | ObjectEntry *prev = &store_header_->object_entries[prev_object_index]; 408 | prev->next = next_object_index; 409 | 410 | if (next_object_index >= 0) { 411 | ObjectEntry *next = &store_header_->object_entries[next_object_index]; 412 | next->prev = prev_object_index; 413 | } 414 | } 415 | dealloc_object_entry(object_index); 416 | 417 | return 0; 418 | } 419 | 420 | void LightningStore::listener() { 421 | int server_fd = socket(AF_UNIX, SOCK_STREAM, 0); 422 | if (server_fd < 0) { 423 | perror("cannot create socket"); 424 | exit(-1); 425 | } 426 | 427 | struct sockaddr_un addr; 428 | memset(&addr, 0, sizeof(addr)); 429 | addr.sun_family = AF_UNIX; 430 | strncpy(addr.sun_path, unix_socket_.c_str(), unix_socket_.size()); 431 | unlink(unix_socket_.c_str()); 432 | 433 | int status = bind(server_fd, (struct sockaddr *)&addr, sizeof(addr)); 434 | if (status < 0) { 435 | perror("cannot bind"); 436 | exit(-1); 437 | } 438 | 439 | status = listen(server_fd, 0); 440 | if (status < 0) { 441 | perror("cannot listen"); 442 | exit(-1); 443 | } 444 | 445 | std::cout << "Store is ready!" << std::endl; 446 | 447 | while (true) { 448 | int client_fd = accept(server_fd, nullptr, nullptr); 449 | if (client_fd < 0) { 450 | perror("cannot accept"); 451 | exit(-1); 452 | } 453 | 454 | pid_t pid; 455 | int bytes_read = recv(client_fd, &pid, sizeof(pid), 0); 456 | if (bytes_read != sizeof(pid)) { 457 | perror("failure reading pid from unix domain socket!"); 458 | exit(-1); 459 | } 460 | 461 | int bytes_sent = send(client_fd, &size_, sizeof(size_), 0); 462 | if (bytes_sent != sizeof(size_)) { 463 | perror("failure sending the size of the object store"); 464 | exit(-1); 465 | } 466 | 467 | int password_length = 0; 468 | 469 | bytes_read = recv(client_fd, &password_length, sizeof(password_length), 0); 470 | if (bytes_read != sizeof(password_length)) { 471 | std::cerr << "failure receiving the password size" << std::endl; 472 | exit(-1); 473 | } 474 | 475 | char password[100]; 476 | bytes_read = recv(client_fd, password, password_length, 0); 477 | if (bytes_read != password_length) { 478 | std::cerr << "failure receiving the password" << std::endl; 479 | exit(-1); 480 | } 481 | 482 | bool ok = false; 483 | 484 | if (strcmp(password, "password") == 0) { 485 | ok = true; 486 | } 487 | 488 | bytes_sent = send(client_fd, &ok, sizeof(ok), 0); 489 | if (bytes_sent != sizeof(ok)) { 490 | perror("failure sending the ok bit"); 491 | exit(-1); 492 | } 493 | 494 | send_fd(client_fd, store_fd_); 495 | { 496 | std::lock_guard guard(client_lock_); 497 | 498 | clients_.insert(pid); 499 | } 500 | } 501 | } 502 | 503 | void LightningStore::Run() { 504 | std::thread monitor_thread = std::thread(&LightningStore::monitor, this); 505 | std::thread listener_thread = std::thread(&LightningStore::listener, this); 506 | listener_thread.join(); 507 | monitor_thread.join(); 508 | } 509 | 510 | int main() { 511 | if (signal(SIGINT, signal_handler) == SIG_ERR) { 512 | std::cerr << "cannot register signal handler!" << std::endl; 513 | exit(-1); 514 | } 515 | 516 | LightningStore store("/tmp/lightning", 1024 * 1024 * 1024); 517 | store.Run(); 518 | 519 | return 0; 520 | } 521 | -------------------------------------------------------------------------------- /test/benchmark.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include "client.h" 13 | 14 | void test(LightningClient &client, int object_size) { 15 | char *a = new char[object_size]; 16 | for (int i = 0; i < object_size; i++) { 17 | a[i] = 'a'; 18 | } 19 | 20 | uint64_t num_tests = 100; 21 | 22 | std::cout << object_size << ", "; 23 | 24 | auto start = std::chrono::high_resolution_clock::now(); 25 | 26 | for (uint64_t i = 0; i < num_tests; i++) { 27 | uint8_t *ptr; 28 | int status = client.Create(i, &ptr, object_size); 29 | memcpy(ptr, a, object_size); 30 | status = client.Seal(i); 31 | } 32 | 33 | auto end = std::chrono::high_resolution_clock::now(); 34 | 35 | std::chrono::duration duration = end - start; 36 | 37 | std::cout << duration.count() / num_tests << ", "; 38 | 39 | char *out; 40 | size_t size; 41 | 42 | start = std::chrono::high_resolution_clock::now(); 43 | for (uint64_t i = 0; i < num_tests; i++) { 44 | int status = client.Get(i, (uint8_t **)&out, &size); 45 | } 46 | 47 | end = std::chrono::high_resolution_clock::now(); 48 | 49 | duration = end - start; 50 | 51 | std::cout << duration.count() / num_tests << ", "; 52 | 53 | start = std::chrono::high_resolution_clock::now(); 54 | for (uint64_t i = 0; i < num_tests; i++) { 55 | int status = client.Delete(i); 56 | } 57 | 58 | end = std::chrono::high_resolution_clock::now(); 59 | 60 | duration = end - start; 61 | 62 | std::cout << duration.count() / num_tests << std::endl; 63 | 64 | delete[] a; 65 | } 66 | 67 | int main(int argc, char **argv) { 68 | LightningClient client("/tmp/lightning", "password"); 69 | 70 | srand(getpid()); 71 | 72 | int test_runs = 100; 73 | 74 | for (int i = 0; i < test_runs; i++) { 75 | int object_size = 16; 76 | while (object_size <= 1024 * 1024) { 77 | test(client, object_size); 78 | object_size *= 2; 79 | } 80 | } 81 | 82 | return 0; 83 | } 84 | -------------------------------------------------------------------------------- /test/create_latency.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include "client.h" 14 | 15 | int main() { 16 | LightningClient client("/tmp/lightning", "password"); 17 | 18 | char a[16]; 19 | for (int i = 0; i < 16; i++) 20 | a[i] = 'a'; 21 | 22 | uint64_t num_tests = 100; 23 | 24 | auto start = std::chrono::high_resolution_clock::now(); 25 | 26 | for (uint64_t i = 0; i < num_tests; i++) { 27 | uint8_t *ptr; 28 | int status = client.Create(i, &ptr, 16); 29 | memcpy(ptr, a, 16); 30 | status = client.Seal(i); 31 | } 32 | 33 | auto end = std::chrono::high_resolution_clock::now(); 34 | std::chrono::duration duration = end - start; 35 | 36 | std::cout << duration.count()/num_tests << std::endl; 37 | 38 | return 0; 39 | } -------------------------------------------------------------------------------- /test/create_object.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include "client.h" 13 | 14 | int main(int argc, char** argv) { 15 | int num_objects = atoi(argv[1]); 16 | 17 | LightningClient client("/tmp/lightning", "password"); 18 | 19 | char a[1024]; 20 | for (int i = 0; i < 1024; i++) 21 | a[i] = 'a'; 22 | 23 | for (uint64_t i = 0; i < num_objects; i++) { 24 | uint8_t *ptr; 25 | int status = client.Create(i, &ptr, 1024); 26 | memcpy(ptr, a, 1024); 27 | status = client.Seal(i); 28 | } 29 | return 0; 30 | } -------------------------------------------------------------------------------- /test/mp_benchmark.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include "client.h" 14 | 15 | void test(LightningClient &client, int object_size, int num_tests, int test_id, const std::vector &object_ids) { 16 | char *a = new char[object_size]; 17 | for (int i = 0; i < object_size; i++) { 18 | a[i] = 'a'; 19 | } 20 | 21 | std::cout << object_size << ", "; 22 | 23 | auto start = std::chrono::high_resolution_clock::now(); 24 | 25 | for (uint64_t i = 0; i < num_tests; i++) { 26 | uint8_t *ptr; 27 | int status = client.Create(object_ids[i], &ptr, object_size); 28 | memcpy(ptr, a, object_size); 29 | status = client.Seal(object_ids[i]); 30 | char *out; 31 | size_t size; 32 | status = client.Get(object_ids[i], (uint8_t **)&out, &size); 33 | status = client.Delete(object_ids[i]); 34 | 35 | } 36 | 37 | auto end = std::chrono::high_resolution_clock::now(); 38 | 39 | std::chrono::duration duration = end - start; 40 | 41 | double time = duration.count(); 42 | 43 | std::cout << num_tests/time << std::endl; 44 | 45 | delete[] a; 46 | } 47 | 48 | int main(int argc, char **argv) { 49 | LightningClient client("/tmp/lightning", "password"); 50 | 51 | int test_id = atoi(argv[1]); 52 | 53 | srand(getpid()); 54 | 55 | std::vector object_ids; 56 | int num_tests = 10000; 57 | object_ids.reserve(num_tests); 58 | for (int i=0;i 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include "client.h" 15 | 16 | void subscribe(LightningClient* client, uint64_t object_id) { 17 | client->Subscribe(object_id); 18 | } 19 | 20 | int main(int argc, char **argv) { 21 | LightningClient client("/tmp/lightning", "password"); 22 | std::vector threads; 23 | threads.reserve(100); 24 | 25 | for (uint64_t object_id = 0; object_id < 100; object_id ++) { 26 | threads.emplace_back(subscribe, &client, object_id); 27 | } 28 | 29 | for (auto& thread : threads) { 30 | thread.join(); 31 | } 32 | 33 | return 0; 34 | } -------------------------------------------------------------------------------- /test/test_cleanup.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include "client.h" 15 | 16 | int main(int argc, char **argv) { 17 | int object_size = 100; 18 | char a[100]; 19 | pid_t child_pid = fork(); 20 | if (child_pid == 0) { 21 | LightningClient client("/tmp/lightning", "password"); 22 | uint8_t *ptr; 23 | int status = client.Create(123, &ptr, object_size); 24 | assert(status == 0); 25 | memcpy(ptr, a, object_size); 26 | status = client.Seal(123); 27 | assert(status == 0); 28 | } else { 29 | sleep(2); 30 | LightningClient client("/tmp/lightning", "password"); 31 | int status; 32 | pid_t pid = wait(&status); 33 | assert (pid == child_pid); 34 | assert (status == 0); 35 | sleep(2); 36 | char *out; 37 | size_t size; 38 | status = client.Get(123, (uint8_t **)&out, &size); 39 | assert (status == -1); 40 | } 41 | 42 | return 0; 43 | } -------------------------------------------------------------------------------- /verifier/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required (VERSION 2.8) 2 | project (LocalStoreVerifier) 3 | set (LocalStoreVerifier_VERSION_MAJOR 0) 4 | set (LocalStoreVerifier_VERSION_MINOR 2) 5 | set (CMAKE_EXPORT_COMPILE_COMMANDS ON) 6 | 7 | # find_package(SWIG REQUIRED) 8 | # include(${SWIG_USE_FILE}) 9 | find_package(Z3 CONFIG) 10 | find_package(LLVM REQUIRED CONFIG) 11 | #find_package(Python3 REQUIRED Development) 12 | find_package(PythonLibs 3 REQUIRED) 13 | 14 | link_directories("/usr/lib/") 15 | link_directories("/usr/local/lib/") 16 | 17 | # find_package(PythonLibs) 18 | # include_directories(${PYTHON_INCLUDE_PATH}) 19 | 20 | include_directories(${LLVM_INCLUDE_DIRS}) 21 | add_definitions(${LLVM_DEFINITIONS}) 22 | llvm_map_components_to_libnames(llvm_libs support core irreader) 23 | 24 | include_directories("${PROJECT_BINARY_DIR}") 25 | include_directories("${PROJECT_SOURCE_DIR}/inc") 26 | set(LIB_SRC_DIR "${PROJECT_SOURCE_DIR}/src") 27 | 28 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++1z") 29 | set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -O0 -g") 30 | set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O3") 31 | 32 | file(GLOB LIB_SRC 33 | "${LIB_SRC_DIR}/*.cc" 34 | ) 35 | 36 | add_library(cobble STATIC ${LIB_SRC}) 37 | # add_library(cobbleso SHARED ${LIB_SRC}) 38 | 39 | target_link_libraries(cobble z3) 40 | target_link_libraries(cobble "-L/usr/lib -lz3") 41 | target_link_libraries(cobble ${llvm_libs}) 42 | target_link_libraries(cobble ${PYTHON_LIBRARY}) 43 | target_link_libraries(cobble elf) 44 | 45 | # target_link_libraries(cobbleso z3) 46 | # target_link_libraries(cobbleso "-L/usr/lib -lz3") 47 | # target_link_libraries(cobbleso ${llvm_libs}) 48 | # target_link_libraries(cobbleso ${PYTHON_LIBRARY}) 49 | # target_link_libraries(cobbleso elf) 50 | 51 | 52 | # set_property(SOURCE ${PROJECT_SOURCE_DIR}/mymod.i PROPERTY CPLUSPLUS ON) 53 | # swig_add_module(mymod python ${PROJECT_SOURCE_DIR}/mymod.i ${LIB_SRC}) 54 | # swig_link_libraries(mymod ${PYTHON_LIBRARIES} doubleclick z3 elf) 55 | 56 | file(GLOB EXEC_SRC 57 | "${PROJECT_SOURCE_DIR}/exec/*.cc" 58 | ) 59 | 60 | foreach(src_file ${EXEC_SRC}) 61 | get_filename_component(prog_name ${src_file} NAME_WE) 62 | add_executable(${prog_name} ${src_file}) 63 | target_link_libraries(${prog_name} ${llvm_libs}) 64 | target_link_libraries(${prog_name} z3) 65 | target_link_libraries(${prog_name} elf) 66 | target_link_libraries(${prog_name} cobble) 67 | endforeach(src_file ${EXEC_SRC}) 68 | -------------------------------------------------------------------------------- /verifier/exec/print_all_funcs.cc: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "llvm-helpers.h" 4 | 5 | int main(int argc, char *argv[]) { 6 | if (argc < 2) { 7 | std::cout << "Usage " << argv[0] << " " << std::endl; 8 | return -1; 9 | } 10 | auto fn = std::string(argv[1]); 11 | llvm::LLVMContext ctx; 12 | llvm::SMDiagnostic err; 13 | 14 | auto m = llvm::parseIRFile(fn, err, ctx); 15 | if (m == nullptr) { 16 | std::cerr << err.getMessage().str() << std::endl; 17 | assert(false && "parseIRFile failed"); 18 | } 19 | 20 | for (auto iter = m->begin(); iter != m->end(); iter++) { 21 | if (iter->isDeclaration()) { 22 | continue; 23 | } 24 | auto fn = iter->getName().str(); 25 | std::cout << fn << std::endl; 26 | } 27 | return 0; 28 | } 29 | -------------------------------------------------------------------------------- /verifier/exec/print_class_fields.cc: -------------------------------------------------------------------------------- 1 | #include "llvm-helpers.h" 2 | #include "llvm-incl.h" 3 | #include "utils.h" 4 | 5 | std::string indent(int lvl) { 6 | std::string result = ""; 7 | for (int i = 0; i < lvl; i++) { 8 | result = result + " "; 9 | } 10 | return result; 11 | } 12 | 13 | void print_element_state(llvm::Module *m, llvm::Type *t, int off, int lvl = 0, 14 | bool include_header = true, bool no_newline = false) { 15 | auto dl = std::make_shared(m); 16 | if (include_header) { 17 | std::cout << indent(lvl) << off << " | "; 18 | } 19 | if (t->isPointerTy()) { 20 | auto et = t->getPointerElementType(); 21 | std::cout << "ptr<" << get_type_name(et) << ">"; 22 | } else if (t->isStructTy()) { 23 | llvm::StructType *st = llvm::dyn_cast(t); 24 | auto sl = dl->getStructLayout(st); 25 | std::cout << "[STRUCT]" << std::endl; 26 | for (auto i = 0; i < st->getNumElements(); i++) { 27 | auto field_t = st->getElementType(i); 28 | auto field_off = sl->getElementOffset(i); 29 | print_element_state(m, field_t, off + field_off, lvl + 1, true, false); 30 | } 31 | no_newline = true; 32 | } else if (t->isArrayTy()) { 33 | auto num_element = t->getArrayNumElements(); 34 | auto et = t->getArrayElementType(); 35 | print_element_state(m, et, off, lvl, false, true); 36 | std::cout << " * " << num_element; 37 | } else if (t->isIntegerTy()) { 38 | auto size = dl->getTypeStoreSize(t); 39 | std::cout << "Integer(" << size << ")"; 40 | } 41 | 42 | if (!no_newline) { 43 | std::cout << std::endl; 44 | } 45 | } 46 | 47 | int main(int argc, char *argv[]) { 48 | if (argc != 3) { 49 | std::cerr << "Usage: " << argv[0] << " " 50 | << " " << std::endl; 51 | return -1; 52 | } 53 | 54 | const std::string ir_filename = std::string(argv[1]); 55 | const std::string class_name = std::string(argv[2]); 56 | 57 | llvm::LLVMContext ctx; 58 | llvm::SMDiagnostic err; 59 | auto module = llvm::parseIRFile(ir_filename, err, ctx); 60 | 61 | if (module == nullptr) { 62 | std::cerr << "failed to parse IR file" << std::endl; 63 | std::cerr << err.getMessage().str() << std::endl; 64 | return -1; 65 | } 66 | 67 | auto structs = module->getIdentifiedStructTypes(); 68 | llvm::StructType *element_t = nullptr; 69 | for (auto &s : structs) { 70 | if (s->getName() == "class." + class_name) { 71 | element_t = s; 72 | break; 73 | } 74 | if (s->getName() == "struct." + class_name) { 75 | element_t = s; 76 | break; 77 | } 78 | } 79 | 80 | if (element_t == nullptr) { 81 | std::cout << "Error: could not find element class definition" << std::endl; 82 | std::cout << "Found classes: " << std::endl; 83 | for (auto &s : structs) { 84 | std::cout << s->getName().str() << std::endl; 85 | } 86 | return -1; 87 | } 88 | 89 | // print element state recursively (with offsets) 90 | print_element_state(module.get(), element_t, 0); 91 | return 0; 92 | } 93 | -------------------------------------------------------------------------------- /verifier/inc/abstract-functions.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "executor.h" 6 | #include "llvm-helpers.h" 7 | #include "llvm-incl.h" 8 | #include "symbolic-expr.h" 9 | 10 | extern std::string log_write_cnt_name; 11 | 12 | class SymExecFunctions { 13 | public: 14 | static std::unique_ptr instance; 15 | 16 | static SymExecFunctions *get(); 17 | 18 | bool is_abstract_function(const std::string &func_name); 19 | 20 | StatePtrList run_function(std::shared_ptr s, 21 | const std::string &func_name, 22 | const std::string &dst_reg, 23 | const std::vector ¶ms); 24 | 25 | void add_readonly_function(const std::string &func_name); 26 | 27 | std::string log_write_func; 28 | std::unordered_map log_write_bounds; 29 | 30 | bool counting_mode = false; 31 | 32 | private: 33 | std::unordered_set readonly_funcs_; 34 | 35 | NameFactory name_gen_; 36 | }; 37 | -------------------------------------------------------------------------------- /verifier/inc/data-structures.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "executor.h" 7 | #include "symbolic-expr.h" 8 | 9 | struct PointeeAccessError { 10 | std::string msg; 11 | }; 12 | 13 | enum class PointeeType { 14 | PointerStore, 15 | Vector, 16 | HashMap, 17 | Buffer, 18 | Packet, 19 | Object, 20 | Invalid, 21 | }; 22 | 23 | std::ostream &operator<<(std::ostream &os, const PointeeType &t); 24 | 25 | class Pointee { 26 | public: 27 | virtual bool is_abstract() const { return false; } 28 | virtual bool is_plain_mem() const { return false; } 29 | virtual RegValue handle_req(const std::string &method_name, 30 | const std::vector &args, 31 | std::shared_ptr ctx) = 0; 32 | virtual RegValue load(Symbolic::ExprPtr off, uint64_t size) const = 0; 33 | virtual void store(Symbolic::ExprPtr off, RegValue val) = 0; 34 | 35 | virtual std::shared_ptr copy_self() const = 0; 36 | 37 | virtual void print(std::ostream &os) const = 0; 38 | virtual PointeeType type() const { return PointeeType::Invalid; } 39 | 40 | std::string name; 41 | 42 | bool modified = false; 43 | }; 44 | 45 | // a buffer that stores a single pointer 46 | class PointerStore : public Pointee { 47 | public: 48 | PointerStore(const std::string &n) : PointerStore(n, 1) {} 49 | PointerStore(const std::string &n, const SymPointer &p) { 50 | name = n; 51 | ptrs.push_back(p); 52 | num_ptrs = 1; 53 | } 54 | PointerStore(const std::string &n, int num_ptrs) { 55 | name = n; 56 | num_ptrs = num_ptrs; 57 | for (int i = 0; i < num_ptrs; i++) { 58 | SymPointer ptr("", nullptr, mk_bv_var(64, name + std::to_string(i))); 59 | ptrs.push_back(ptr); 60 | } 61 | } 62 | virtual bool is_abstract() const { return true; } 63 | virtual bool is_plain_mem() const { return false; } 64 | virtual RegValue handle_req(const std::string &method_name, 65 | const std::vector &args, 66 | std::shared_ptr ctx) { 67 | throw "pointer store cannot handle request"; 68 | } 69 | 70 | virtual RegValue load(Symbolic::ExprPtr off, uint64_t size) const; 71 | virtual void store(Symbolic::ExprPtr off, RegValue val); 72 | 73 | virtual std::shared_ptr copy_self() const { 74 | return std::make_shared(*this); 75 | } 76 | 77 | virtual void print(std::ostream &os) const; 78 | virtual PointeeType type() const { return PointeeType::PointerStore; } 79 | 80 | SymPointer load_ptr(int idx = 0) const { return ptrs[idx]; } 81 | void store_ptr(const SymPointer &p, int idx = 0) { ptrs[idx] = p; } 82 | 83 | struct MultiPtrEntry { 84 | Symbolic::ExprPtr pre_cond; 85 | SymPointer ptr; 86 | }; 87 | using MultiPtr = std::vector; 88 | MultiPtr load_ptr(Symbolic::ExprPtr off) const; 89 | 90 | std::vector ptrs; 91 | 92 | int num_ptrs; 93 | }; 94 | 95 | class AbstractFunc { 96 | public: 97 | virtual bool match(const std::string &fn) const = 0; 98 | 99 | using ResultT = std::vector>; 100 | virtual ResultT call(const std::string &func_name, 101 | const std::vector ¶ms, 102 | std::shared_ptr state, 103 | const std::string &dst_reg) = 0; 104 | }; 105 | 106 | class Inaccessible : public Pointee { 107 | public: 108 | Inaccessible(const std::string &name); 109 | 110 | virtual RegValue handle_req(const std::string &method_name, 111 | const std::vector &args, 112 | std::shared_ptr ctx) override { 113 | throw ExecError{"Inaccessible"}; 114 | } 115 | virtual RegValue load(Symbolic::ExprPtr off, uint64_t size) const override { 116 | throw ExecError{"Inaccessible"}; 117 | } 118 | virtual void store(Symbolic::ExprPtr off, RegValue val) override { 119 | throw ExecError{"Inaccessible"}; 120 | } 121 | 122 | virtual std::shared_ptr copy_self() const override { 123 | auto ptr = std::make_shared(*this); 124 | return std::dynamic_pointer_cast(ptr); 125 | } 126 | 127 | virtual void print(std::ostream &os) const override; 128 | }; 129 | 130 | class Buffer : public Pointee { 131 | public: 132 | Buffer(const std::string &name); 133 | Buffer(const std::string &name, int size, int cell_size = 1); 134 | virtual bool is_plain_mem() const override { return true; } 135 | 136 | virtual RegValue handle_req(const std::string &method_name, 137 | const std::vector &args, 138 | std::shared_ptr ctx) override { 139 | throw ExecError{"Buffer could not handle request"}; 140 | } 141 | virtual RegValue load(Symbolic::ExprPtr off, uint64_t size) const override; 142 | virtual void store(Symbolic::ExprPtr off, RegValue val) override; 143 | 144 | RegValue load_be(Symbolic::ExprPtr off, uint64_t size) const; 145 | void store_be(Symbolic::ExprPtr off, RegValue val); 146 | 147 | virtual std::shared_ptr copy_self() const override { 148 | auto result = std::make_shared(*this); 149 | return std::dynamic_pointer_cast(result); 150 | } 151 | virtual void print(std::ostream &os) const override; 152 | Symbolic::ExprPtr equals(Buffer &buf) const; 153 | 154 | virtual PointeeType type() const override { return PointeeType::Buffer; } 155 | 156 | std::shared_ptr content_f; 157 | bool sized; 158 | int size; 159 | 160 | int cell_size = 1; 161 | 162 | bool have_write_back = false; 163 | std::function, std::shared_ptr)> 164 | write_back_fn; 165 | }; 166 | 167 | class ConcreteCacheBuffer : public Buffer { 168 | public: 169 | ConcreteCacheBuffer(const std::string &name); 170 | ConcreteCacheBuffer(const std::string &name, int size); 171 | virtual bool is_plain_mem() const override { return true; } 172 | 173 | virtual RegValue handle_req(const std::string &method_name, 174 | const std::vector &args, 175 | std::shared_ptr ctx) override { 176 | throw ExecError{"Buffer could not handle request"}; 177 | } 178 | virtual RegValue load(Symbolic::ExprPtr off, uint64_t size) const override; 179 | virtual void store(Symbolic::ExprPtr off, RegValue val) override; 180 | 181 | RegValue load_be(Symbolic::ExprPtr off, uint64_t size) const; 182 | void store_be(Symbolic::ExprPtr off, RegValue val); 183 | 184 | virtual std::shared_ptr copy_self() const override { 185 | auto result = std::make_shared(*this); 186 | return std::dynamic_pointer_cast(result); 187 | } 188 | virtual void print(std::ostream &os) const override; 189 | Symbolic::ExprPtr equals(ConcreteCacheBuffer &buf) const { return nullptr; } 190 | 191 | virtual PointeeType type() const override { return PointeeType::Buffer; } 192 | 193 | struct CacheRegion { 194 | uint64_t off; 195 | size_t size; 196 | RegValue val; 197 | }; 198 | 199 | std::vector cache; 200 | 201 | std::shared_ptr content_f; 202 | bool sized; 203 | int size; 204 | 205 | bool have_write_back = false; 206 | std::function, std::shared_ptr)> 207 | write_back_fn; 208 | }; 209 | 210 | class AbstractType : public Pointee { 211 | public: 212 | virtual bool is_abstract() const override { return true; } 213 | virtual RegValue load(Symbolic::ExprPtr off, uint64_t size) const override { 214 | throw ExecError{ 215 | "Could not perform load / store on abstract data structure"}; 216 | } 217 | virtual void store(Symbolic::ExprPtr off, RegValue val) override { 218 | throw ExecError{ 219 | "Could not perform load / store on abstract data structure"}; 220 | } 221 | }; 222 | 223 | class AbstractVector : public AbstractType { 224 | public: 225 | AbstractVector(const std::string &name, 226 | std::shared_ptr ele_type); 227 | AbstractVector(const std::string &name, 228 | std::shared_ptr ele_type, 229 | Symbolic::ExprPtr n_elements); 230 | AbstractVector(const std::string &name, 231 | std::shared_ptr ele_type, uint64_t n_elements); 232 | virtual RegValue handle_req(const std::string &method_name, 233 | const std::vector &args, 234 | std::shared_ptr ctx) override; 235 | 236 | virtual std::shared_ptr copy_self() const override { 237 | auto result = std::make_shared(*this); 238 | return std::dynamic_pointer_cast(result); 239 | } 240 | 241 | virtual void print(std::ostream &os) const override; 242 | 243 | std::shared_ptr get(Symbolic::ExprPtr idx) const; 244 | void set(Symbolic::ExprPtr idx, Symbolic::ExprPtr val); 245 | void push_back(Symbolic::ExprPtr val); 246 | 247 | bool bound_check(Symbolic::ExprPtr idx) const; 248 | virtual PointeeType type() const override { return PointeeType::Vector; } 249 | std::shared_ptr arr_f; 250 | std::shared_ptr val_type; 251 | Symbolic::ExprPtr n_elements; 252 | }; 253 | 254 | class AbstractMap : public AbstractType { 255 | public: 256 | AbstractMap(const std::string &name, 257 | const Symbolic::PtrList &key_types, 258 | const Symbolic::PtrList &val_types); 259 | virtual RegValue handle_req(const std::string &method_name, 260 | const std::vector &args, 261 | std::shared_ptr ctx) override; 262 | 263 | virtual std::shared_ptr copy_self() const override { 264 | auto result = std::make_shared(*this); 265 | return std::dynamic_pointer_cast(result); 266 | } 267 | 268 | std::vector> key_types; 269 | std::vector> val_types; 270 | std::shared_ptr contains_f; 271 | std::vector> val_f; 272 | 273 | Symbolic::ExprPtr contains(const Symbolic::OpApplyNode::ArgList &args) const; 274 | std::vector 275 | get_vals(const Symbolic::OpApplyNode::ArgList &args) const; 276 | void set_vals(const std::vector &args, 277 | const std::vector &vals); 278 | void delete_val(const Symbolic::OpApplyNode::ArgList &args); 279 | 280 | virtual void print(std::ostream &os) const override; 281 | virtual PointeeType type() const override { return PointeeType::HashMap; } 282 | }; 283 | 284 | class Packet : public AbstractType { 285 | public: 286 | Packet(const std::string &name, std::shared_ptr state); 287 | 288 | virtual RegValue handle_req(const std::string &method_name, 289 | const std::vector &args, 290 | std::shared_ptr ctx); 291 | 292 | virtual std::shared_ptr copy_self() const { 293 | auto ptr = std::make_shared(*this); 294 | return std::dynamic_pointer_cast(ptr); 295 | } 296 | 297 | virtual void print(std::ostream &os) const; 298 | virtual PointeeType type() const { return PointeeType::Packet; } 299 | 300 | std::string anno_buf_name; 301 | Symbolic::ExprPtr len; 302 | std::string content_buf_name; 303 | }; 304 | 305 | // This is essentially a buffer with certain regions blocked 306 | class AbstractObject : public AbstractType { 307 | public: 308 | AbstractObject(const std::string &name); 309 | virtual RegValue handle_req(const std::string &method_name, 310 | const std::vector &args, 311 | std::shared_ptr ctx) override; 312 | 313 | virtual std::shared_ptr copy_self() const override; 314 | 315 | virtual void print(std::ostream &os) const override; 316 | virtual PointeeType type() const override { return PointeeType::Object; } 317 | 318 | struct Region { 319 | enum class T { 320 | INVALID, 321 | INLINED, // inlined means that this object is a part of the object, 322 | // instead of a pointer 323 | }; 324 | 325 | T type; 326 | uint64_t start_off; 327 | uint64_t size; 328 | 329 | SymPointer ptr; 330 | 331 | Region() {} 332 | Region(T t, uint64_t start, uint64_t sz, SymPointer p) 333 | : type(t), start_off(start), size(sz), ptr(p) {} 334 | }; 335 | 336 | /* return value: 337 | * 0 : found a region 338 | * -1 : not accessible region 339 | */ 340 | int find_region(uint64_t off, Region &r) const; 341 | int find_region(Symbolic::ExprPtr off, Region &r, 342 | Symbolic::ExprPtr pre_cond = nullptr) const; 343 | 344 | struct FindResultEntry { 345 | Symbolic::ExprPtr pre_cond; 346 | Region region; 347 | }; 348 | 349 | std::vector find_region(Symbolic::ExprPtr off) const; 350 | 351 | void add_region(const Region &r); 352 | 353 | void add_ptr_at(std::shared_ptr s, const SymPointer &ptr, 354 | uint64_t off); 355 | void add_obj_ptr_at(std::shared_ptr s, 356 | std::shared_ptr obj, uint64_t off); 357 | 358 | protected: 359 | std::vector regions; 360 | }; 361 | -------------------------------------------------------------------------------- /verifier/inc/executor.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "llvm-helpers.h" 9 | #include "llvm-incl.h" 10 | #include "symbolic-expr.h" 11 | #include "utils.h" 12 | #include "z3-gen.h" 13 | 14 | class Pointee; 15 | class Buffer; 16 | 17 | class IRDataBase { 18 | public: 19 | IRDataBase(); 20 | 21 | void load_ir_file(const std::string &ir_filename); 22 | 23 | llvm::Function *get_fn_by_name(const std::string &func_name) const; 24 | llvm::Function *get_fn_by_prefix(const std::string &prefix) const; 25 | 26 | llvm::LLVMContext &get_ctx() const; 27 | 28 | protected: 29 | llvm::LLVMContext ctx_; 30 | llvm::SMDiagnostic err_; 31 | 32 | std::unordered_map> modules_; 33 | }; 34 | 35 | std::pair get_inst_loc(const llvm::Instruction &inst); 36 | 37 | struct SymPointer { 38 | std::string pointer_base; 39 | llvm::Type *llvm_type = nullptr; 40 | 41 | bool is_shm_ptr = false; 42 | bool is_log_ptr = false; 43 | 44 | Symbolic::ExprPtr offset; 45 | 46 | SymPointer() {} 47 | 48 | SymPointer(const std::string &base); 49 | SymPointer(const std::string &base, llvm::Type *t, Symbolic::ExprPtr off); 50 | }; 51 | 52 | struct RegValue { 53 | std::variant content; 54 | 55 | bool is_val() const; 56 | bool is_ptr() const; 57 | 58 | Symbolic::ExprPtr &get_val(); 59 | SymPointer &get_ptr(); 60 | 61 | const SymPointer &get_ptr() const; 62 | const Symbolic::ExprPtr &get_val() const; 63 | }; 64 | 65 | struct ExecError { 66 | std::string msg; 67 | 68 | std::string exception_file; 69 | int exception_line; 70 | 71 | std::string file_name; 72 | int line_number; 73 | 74 | ExecError(); 75 | ExecError(const std::string &msg); 76 | ExecError(const llvm::Instruction &inst, const std::string &msg); 77 | }; 78 | 79 | struct ExecutionState { 80 | llvm::BasicBlock::iterator inst_iter; 81 | llvm::BasicBlock::iterator bb_end; 82 | 83 | llvm::BasicBlock *prev_bb = nullptr; 84 | 85 | std::vector pre_cond_list; 86 | 87 | std::unordered_map registers; 88 | std::unordered_map> objects; 89 | 90 | std::string shm_mem_name; 91 | std::shared_ptr shm_mem; 92 | 93 | std::string log_mem_name; 94 | std::shared_ptr log_mem; 95 | 96 | NameFactory &name_gen; 97 | RegValue ret_val; 98 | 99 | std::vector> crashed_states; 100 | 101 | bool finished_execution = false; 102 | bool have_new_cond = false; 103 | 104 | bool is_assert_fail = false; 105 | std::string fail_msg; 106 | 107 | enum class ValidT { 108 | VALID, 109 | INVALID, 110 | UNKNOWN, 111 | }; 112 | 113 | ValidT valid = ValidT::UNKNOWN; 114 | 115 | ExecutionState(NameFactory &name_gen); 116 | ExecutionState(NameFactory &name_gen, llvm::Function *f); 117 | ExecutionState(const ExecutionState &s); 118 | 119 | std::shared_ptr copy_self() const { 120 | auto c = std::make_shared(*this); 121 | c->shm_mem = std::dynamic_pointer_cast(c->objects[shm_mem_name]); 122 | c->log_mem = std::dynamic_pointer_cast(c->objects[log_mem_name]); 123 | // std::cout << "after copy: " << std::endl; 124 | // for (auto &kv : c->objects) { 125 | // std::cout << kv.first << " : " << kv.second.get() << std::endl; 126 | // } 127 | return c; 128 | } 129 | 130 | RegValue get_reg_val(const llvm::Value &value) const; 131 | RegValue get_reg_val(const std::string ®_name) const; 132 | void set_reg_val(const std::string ®_name, const RegValue &val); 133 | void jump_to_bb(llvm::BasicBlock *bb); 134 | std::shared_ptr find_pointee(const std::string &name) const; 135 | void add_pointee(std::shared_ptr pointee); 136 | void add_crash_point(); 137 | 138 | void init_with_fn(llvm::Function *f); 139 | void init_shm_log(); 140 | void add_pre_cond(Symbolic::ExprPtr c); 141 | Symbolic::ExprPtr get_pre_cond() const; 142 | 143 | // valid means reachable 144 | bool is_valid(); 145 | 146 | // TODO: add states for tracking function contexts for caller 147 | // we need to emulate a function call stack here 148 | struct Context { 149 | // register values 150 | std::unordered_map registers; 151 | 152 | // return address 153 | llvm::BasicBlock::iterator inst_iter; 154 | llvm::BasicBlock::iterator bb_end; 155 | 156 | // return reg 157 | std::string ret_val_reg; 158 | 159 | std::string function_name; 160 | }; 161 | std::stack call_stack; 162 | }; 163 | 164 | struct VerificationResult { 165 | bool have_ce = true; 166 | 167 | VerificationResult(bool found_ce); 168 | }; 169 | 170 | using StatePtrList = std::vector>; 171 | 172 | class CrashVerifier { 173 | public: 174 | CrashVerifier(std::unique_ptr db); 175 | 176 | bool verify_crash_safe(const std::string &fn, const std::string &recover_fn, 177 | std::shared_ptr init_state, 178 | int num_worker = 1); 179 | 180 | StatePtrList run(std::shared_ptr init_state, 181 | int num_worker = 1, bool skip_pre_cond_check = false); 182 | StatePtrList single_step(std::shared_ptr state); 183 | 184 | IRDataBase *irdb() { return irdb_.get(); } 185 | 186 | protected: 187 | std::unique_ptr irdb_; 188 | }; 189 | 190 | class LogOpCounter { 191 | public: 192 | LogOpCounter(std::unique_ptr db); 193 | 194 | StatePtrList run(std::shared_ptr init_state, 195 | int num_worker = 1); 196 | StatePtrList single_step(std::shared_ptr state); 197 | 198 | IRDataBase *irdb() { return irdb_.get(); } 199 | 200 | protected: 201 | std::unique_ptr irdb_; 202 | }; 203 | -------------------------------------------------------------------------------- /verifier/inc/llvm-helpers.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "llvm-incl.h" 6 | 7 | std::vector find_element_entry(llvm::Module *module, 8 | const std::string &element_name); 9 | 10 | std::string get_type_name(llvm::Type *t); 11 | std::string llvm_type_to_str(llvm::Type *t); 12 | uint64_t get_type_size(const llvm::Module *module, llvm::Type *type); 13 | std::string get_name(const llvm::Value &value); 14 | int64_t get_int_val(const llvm::Value *value); 15 | 16 | std::string demangle_cpp_name(const std::string &cpp_name); 17 | 18 | std::vector split_template(const std::string &cpp_id); 19 | -------------------------------------------------------------------------------- /verifier/inc/llvm-incl.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | -------------------------------------------------------------------------------- /verifier/inc/logwrite_bound.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "abstract-functions.h" 7 | #include "executor.h" 8 | #include "z3-gen.h" 9 | 10 | struct LogWriteBound { 11 | int bound; 12 | std::function &)> pre_cond; 13 | std::function &, RegValue &)> 14 | post_cond; 15 | }; 16 | 17 | class BoundRegistry { 18 | public: 19 | static BoundRegistry *get(); 20 | 21 | bool have_record(const std::string &fn) const; 22 | 23 | int find_bound(const std::string &fn, std::shared_ptr &s, 24 | const std::vector ¶ms, LogWriteBound &bound); 25 | void add_bound(const std::string &fn, const LogWriteBound &bound); 26 | 27 | protected: 28 | static std::unique_ptr instance; 29 | std::unordered_map bounds_; 30 | }; 31 | 32 | void verify_num_logwrite_bound(LogOpCounter *verifier, 33 | std::shared_ptr init_state, 34 | int bound); 35 | 36 | void verify_num_logwrite_bound(LogOpCounter *verifier, 37 | std::shared_ptr init_state, 38 | const std::vector ¶ms, 39 | const LogWriteBound &bound); 40 | 41 | void init_execution_state(std::shared_ptr &s); 42 | -------------------------------------------------------------------------------- /verifier/inc/symbolic-expr.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | namespace Symbolic { 12 | 13 | #define DEF_TYPE_PREDICATE_DEFAULT(predicate_name) \ 14 | virtual bool predicate_name() const { return false; } 15 | 16 | struct TypeCheckError { 17 | std::string msg; 18 | }; 19 | 20 | class Type { 21 | public: 22 | virtual bool is_val() const = 0; 23 | virtual bool equal_to(std::shared_ptr t) const = 0; 24 | virtual void print(std::ostream &os) const = 0; 25 | virtual int get_bv_width() const { throw TypeCheckError{"not a bit vector"}; } 26 | virtual bool is_bv_type() const { return false; } 27 | virtual bool is_func_type() const { return false; } 28 | }; 29 | 30 | class ValType : public Type { 31 | public: 32 | enum class T { 33 | BITVEC, 34 | }; 35 | 36 | T type; 37 | 38 | virtual bool is_val() const override { return true; } 39 | }; 40 | 41 | class BitVecType : public ValType { 42 | public: 43 | int bitwidth; 44 | 45 | virtual int get_bv_width() const override { return bitwidth; } 46 | 47 | virtual bool is_bv_type() const override { return true; } 48 | 49 | virtual void print(std::ostream &os) const override; 50 | virtual bool equal_to(std::shared_ptr t) const override; 51 | 52 | BitVecType() : bitwidth(0) {} 53 | BitVecType(int bw) : bitwidth(bw) {} 54 | }; 55 | 56 | class UFType : public Type { 57 | public: 58 | std::vector> key_types; 59 | std::shared_ptr val_type; 60 | 61 | UFType(const std::vector> &kts, 62 | std::shared_ptr vt) 63 | : key_types(kts), val_type(vt) {} 64 | 65 | virtual bool is_val() const override { return false; } 66 | 67 | virtual bool is_func_type() const override { return true; } 68 | 69 | virtual void print(std::ostream &os) const override; 70 | virtual bool equal_to(std::shared_ptr t) const override; 71 | }; 72 | 73 | class Expr; 74 | class Lambda; 75 | class ConcreteBv; 76 | class SymbolicVar; 77 | class AddExpr; 78 | class SubExpr; 79 | class MulExpr; 80 | class DivExpr; 81 | class ModExpr; 82 | class UDivExpr; 83 | class UModExpr; 84 | 85 | class LAndExpr; 86 | class LOrExpr; 87 | class LXorExpr; 88 | class LNotExpr; 89 | class ImpliesExpr; 90 | 91 | class AndExpr; 92 | class OrExpr; 93 | class XorExpr; 94 | class NotExpr; 95 | class LshExpr; 96 | class LRshExpr; 97 | class ARshExpr; 98 | 99 | class EqExpr; 100 | class NeqExpr; 101 | class LeExpr; 102 | class LtExpr; 103 | class GeExpr; 104 | class GtExpr; 105 | class UleExpr; 106 | class UltExpr; 107 | class UgeExpr; 108 | class UgtExpr; 109 | 110 | class ExtractExpr; 111 | class SExtExpr; 112 | class UExtExpr; 113 | 114 | class IteExpr; 115 | class FuncApply; 116 | class ConcatExpr; 117 | 118 | class ForallExpr; 119 | class ExistsExpr; 120 | 121 | #define DECLARE_EXPR_VISITOR(t) \ 122 | virtual void visit_expr(t &expr) { this->visit_expr(*(Expr *)&expr); } 123 | 124 | class ExprVisitor { 125 | public: 126 | virtual void visit(Expr &expr); 127 | 128 | virtual void visit_expr(Expr &e) {} 129 | DECLARE_EXPR_VISITOR(Lambda); 130 | DECLARE_EXPR_VISITOR(ConcreteBv); 131 | DECLARE_EXPR_VISITOR(SymbolicVar); 132 | 133 | DECLARE_EXPR_VISITOR(AddExpr); 134 | DECLARE_EXPR_VISITOR(SubExpr); 135 | DECLARE_EXPR_VISITOR(MulExpr); 136 | DECLARE_EXPR_VISITOR(DivExpr); 137 | DECLARE_EXPR_VISITOR(ModExpr); 138 | DECLARE_EXPR_VISITOR(UDivExpr); 139 | DECLARE_EXPR_VISITOR(UModExpr); 140 | 141 | DECLARE_EXPR_VISITOR(LAndExpr); 142 | DECLARE_EXPR_VISITOR(LOrExpr); 143 | DECLARE_EXPR_VISITOR(LXorExpr); 144 | DECLARE_EXPR_VISITOR(LNotExpr); 145 | DECLARE_EXPR_VISITOR(ImpliesExpr); 146 | 147 | DECLARE_EXPR_VISITOR(AndExpr); 148 | DECLARE_EXPR_VISITOR(OrExpr); 149 | DECLARE_EXPR_VISITOR(XorExpr); 150 | DECLARE_EXPR_VISITOR(NotExpr); 151 | DECLARE_EXPR_VISITOR(LshExpr); 152 | DECLARE_EXPR_VISITOR(LRshExpr); 153 | DECLARE_EXPR_VISITOR(ARshExpr); 154 | 155 | DECLARE_EXPR_VISITOR(EqExpr); 156 | DECLARE_EXPR_VISITOR(NeqExpr); 157 | DECLARE_EXPR_VISITOR(LeExpr); 158 | DECLARE_EXPR_VISITOR(LtExpr); 159 | DECLARE_EXPR_VISITOR(GeExpr); 160 | DECLARE_EXPR_VISITOR(GtExpr); 161 | DECLARE_EXPR_VISITOR(UleExpr); 162 | DECLARE_EXPR_VISITOR(UltExpr); 163 | DECLARE_EXPR_VISITOR(UgeExpr); 164 | DECLARE_EXPR_VISITOR(UgtExpr); 165 | 166 | DECLARE_EXPR_VISITOR(ExtractExpr); 167 | DECLARE_EXPR_VISITOR(SExtExpr); 168 | DECLARE_EXPR_VISITOR(UExtExpr); 169 | 170 | DECLARE_EXPR_VISITOR(IteExpr); 171 | DECLARE_EXPR_VISITOR(FuncApply); 172 | DECLARE_EXPR_VISITOR(ConcatExpr); 173 | 174 | DECLARE_EXPR_VISITOR(ForallExpr); 175 | DECLARE_EXPR_VISITOR(ExistsExpr); 176 | }; 177 | 178 | using ExprPtr = std::shared_ptr; 179 | 180 | #define VISITOR_ACCEPT \ 181 | virtual void accept(ExprVisitor &v) override { v.visit_expr(*this); } 182 | 183 | class Expr : public std::enable_shared_from_this { 184 | public: 185 | std::shared_ptr type; 186 | DEF_TYPE_PREDICATE_DEFAULT(is_symbolic); 187 | DEF_TYPE_PREDICATE_DEFAULT(is_var); 188 | 189 | Expr(); 190 | 191 | virtual std::shared_ptr simplify() { return shared_from_this(); } 192 | 193 | virtual void accept(ExprVisitor &v) { v.visit_expr(*this); } 194 | 195 | boost::uuids::uuid get_uuid() const { return uuid; } 196 | 197 | protected: 198 | boost::uuids::uuid uuid; 199 | }; 200 | 201 | template using PtrList = std::vector>; 202 | 203 | class OpApplyNode : public Expr { 204 | public: 205 | using ArgList = PtrList; 206 | 207 | OpApplyNode(const ArgList &args); 208 | 209 | void type_check(const PtrList &types); 210 | 211 | virtual bool is_symbolic() const override { return is_symbolic_; } 212 | 213 | ArgList simplify_args() const; 214 | 215 | public: 216 | ArgList args_; 217 | bool is_symbolic_; 218 | }; 219 | 220 | class ConcreteVal : public Expr { 221 | public: 222 | }; 223 | 224 | class Lambda : public Expr { 225 | public: 226 | using FuncT = std::function; 227 | FuncT func; 228 | 229 | Lambda(std::shared_ptr func_type, FuncT func); 230 | 231 | Lambda(const PtrList &arg_t, std::shared_ptr ret_t, FuncT func); 232 | VISITOR_ACCEPT; 233 | }; 234 | 235 | class ConcreteBv : public ConcreteVal { 236 | public: 237 | ConcreteBv(int bv_size, uint64_t val) { 238 | auto t = std::make_shared(); 239 | t->bitwidth = bv_size; 240 | type = std::dynamic_pointer_cast(t); 241 | val_ = val; 242 | } 243 | 244 | uint64_t get_val() const { return val_; } 245 | 246 | VISITOR_ACCEPT; 247 | 248 | protected: 249 | uint64_t val_; 250 | }; 251 | 252 | class SymbolicVar : public Expr { 253 | public: 254 | SymbolicVar(std::shared_ptr t, const std::string &n) : name(n) { 255 | type = t; 256 | } 257 | virtual bool is_symbolic() const override { return true; } 258 | virtual bool is_var() const override { return true; } 259 | 260 | VISITOR_ACCEPT; 261 | 262 | std::string name; 263 | }; 264 | 265 | class FuncApply : public OpApplyNode { 266 | public: 267 | FuncApply(ExprPtr func, const ArgList &args); 268 | VISITOR_ACCEPT; 269 | 270 | ExprPtr func; 271 | }; 272 | 273 | class BvBinOpExpr : public OpApplyNode { 274 | public: 275 | BvBinOpExpr(const ArgList &args); 276 | virtual ExprPtr simplify() override; 277 | virtual ExprPtr concrete_binop(uint64_t a1, uint64_t a2) { 278 | return shared_from_this(); 279 | } 280 | }; 281 | 282 | class BvBinPredExpr : public OpApplyNode { 283 | public: 284 | BvBinPredExpr(const ArgList &args); 285 | virtual ExprPtr simplify() override; 286 | virtual ExprPtr concrete_binop(uint64_t a1, uint64_t a2) { 287 | return shared_from_this(); 288 | } 289 | }; 290 | 291 | #define DECLARE_OP(class_name) \ 292 | class class_name : public BvBinOpExpr { \ 293 | public: \ 294 | class_name(const ArgList &args); \ 295 | VISITOR_ACCEPT; \ 296 | virtual ExprPtr concrete_binop(uint64_t a1, uint64_t a2) override; \ 297 | } 298 | 299 | #define DECLARE_PRED(class_name) \ 300 | class class_name : public BvBinPredExpr { \ 301 | public: \ 302 | class_name(const ArgList &args); \ 303 | VISITOR_ACCEPT; \ 304 | virtual ExprPtr concrete_binop(uint64_t a1, uint64_t a2) override; \ 305 | } 306 | 307 | DECLARE_OP(AddExpr); 308 | DECLARE_OP(SubExpr); 309 | DECLARE_OP(MulExpr); 310 | DECLARE_OP(DivExpr); 311 | DECLARE_OP(ModExpr); 312 | DECLARE_OP(UDivExpr); 313 | DECLARE_OP(UModExpr); 314 | 315 | DECLARE_OP(LAndExpr); 316 | DECLARE_OP(LOrExpr); 317 | DECLARE_OP(LXorExpr); 318 | DECLARE_OP(ImpliesExpr); 319 | 320 | DECLARE_OP(AndExpr); 321 | DECLARE_OP(OrExpr); 322 | DECLARE_OP(XorExpr); 323 | DECLARE_OP(LshExpr); 324 | DECLARE_OP(LRshExpr); 325 | DECLARE_OP(ARshExpr); 326 | 327 | DECLARE_PRED(EqExpr); 328 | DECLARE_PRED(NeqExpr); 329 | DECLARE_PRED(LeExpr); 330 | DECLARE_PRED(LtExpr); 331 | DECLARE_PRED(GeExpr); 332 | DECLARE_PRED(GtExpr); 333 | DECLARE_PRED(UleExpr); 334 | DECLARE_PRED(UltExpr); 335 | DECLARE_PRED(UgeExpr); 336 | DECLARE_PRED(UgtExpr); 337 | 338 | class ConcatExpr : public OpApplyNode { 339 | public: 340 | ConcatExpr(const ArgList &args); 341 | VISITOR_ACCEPT; 342 | }; 343 | class LNotExpr : public OpApplyNode { 344 | public: 345 | LNotExpr(ExprPtr e); 346 | VISITOR_ACCEPT; 347 | }; 348 | 349 | class NotExpr : public OpApplyNode { 350 | public: 351 | NotExpr(ExprPtr e); 352 | VISITOR_ACCEPT; 353 | }; 354 | 355 | class ExtractExpr : public Expr { 356 | public: 357 | ExtractExpr(ExprPtr e, int start, int end); 358 | VISITOR_ACCEPT; 359 | 360 | virtual bool is_symbolic() const override { return true; } 361 | 362 | ExprPtr v; 363 | int from, to; 364 | }; 365 | 366 | class SExtExpr : public Expr { 367 | public: 368 | SExtExpr(ExprPtr e, int to); 369 | VISITOR_ACCEPT; 370 | 371 | virtual bool is_symbolic() const override { return true; } 372 | 373 | ExprPtr v; 374 | int to; 375 | }; 376 | 377 | class UExtExpr : public Expr { 378 | public: 379 | UExtExpr(ExprPtr e, int to); 380 | VISITOR_ACCEPT; 381 | 382 | virtual bool is_symbolic() const override { return true; } 383 | 384 | ExprPtr v; 385 | int to; 386 | }; 387 | 388 | class IteExpr : public Expr { 389 | public: 390 | IteExpr(ExprPtr cond, ExprPtr t, ExprPtr f); 391 | VISITOR_ACCEPT; 392 | virtual std::shared_ptr simplify() override; 393 | 394 | virtual bool is_symbolic() const override { return true; } 395 | 396 | ExprPtr cond; 397 | ExprPtr t_val; 398 | ExprPtr f_val; 399 | }; 400 | 401 | class ForallExpr : public Expr { 402 | public: 403 | ForallExpr(const std::vector &vars, ExprPtr cond); 404 | VISITOR_ACCEPT; 405 | virtual std::shared_ptr simplify() override; 406 | 407 | virtual bool is_symbolic() const override { return true; } 408 | 409 | std::vector vars; 410 | ExprPtr cond; 411 | }; 412 | 413 | class ExistsExpr : public Expr { 414 | public: 415 | ExistsExpr(const std::vector &vars, ExprPtr cond); 416 | VISITOR_ACCEPT; 417 | virtual std::shared_ptr simplify() override; 418 | 419 | virtual bool is_symbolic() const override { return true; } 420 | 421 | std::vector vars; 422 | ExprPtr cond; 423 | }; 424 | 425 | #undef DECLARE_PRED 426 | #undef DECLARE_OP 427 | 428 | ExprPtr endian_reverse(ExprPtr val); 429 | 430 | uint64_t get_concrete_val(ExprPtr v); 431 | 432 | void print_expr(ExprPtr expr, std::ostream &os); 433 | } // namespace Symbolic 434 | 435 | std::shared_ptr mk_bv_type(int bitwidth); 436 | 437 | #define mk_expr_ptr(T, ...) \ 438 | std::dynamic_pointer_cast<::Symbolic::Expr>( \ 439 | std::shared_ptr<::Symbolic::T>(new ::Symbolic::T(__VA_ARGS__))) 440 | 441 | std::shared_ptr mk_bv_var(int bitwidth, 442 | const std::string &name); 443 | #define mk_concrete_bv(bw, n) mk_expr_ptr(ConcreteBv, bw, n) 444 | -------------------------------------------------------------------------------- /verifier/inc/utils.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | bool is_prefix(const std::string &s, const std::string &prefix); 11 | 12 | class NameFactory { 13 | std::unordered_map name_map_; 14 | std::mutex name_map_mutex_; 15 | 16 | public: 17 | std::string GetUniqueName(const std::string &base_name); 18 | std::string gen(const std::string &base_name) { 19 | return GetUniqueName(base_name); 20 | } 21 | std::string operator()(const std::string &base_name) { 22 | return GetUniqueName(base_name); 23 | } 24 | }; 25 | 26 | class SubProcessFunc { 27 | public: 28 | struct Exception { 29 | std::string msg; 30 | }; 31 | 32 | SubProcessFunc(std::function f); 33 | std::string operator()(); 34 | 35 | protected: 36 | std::function f_; 37 | }; 38 | -------------------------------------------------------------------------------- /verifier/inc/z3-gen.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "symbolic-expr.h" 8 | 9 | namespace Symbolic { 10 | struct Z3Expr { 11 | std::variant content; 12 | 13 | bool is_expr() const; 14 | bool is_func() const; 15 | bool is_bool() const; 16 | z3::expr get_expr(); 17 | z3::expr get_bool(); 18 | z3::func_decl get_func(); 19 | 20 | Z3Expr() : content(0) {} 21 | }; 22 | struct Z3Context { 23 | z3::context ctx; 24 | std::unordered_map> 26 | cache_; 27 | std::unordered_map var_cache_; 28 | }; 29 | 30 | Z3Expr gen_z3_expr(Z3Context &ctx, ExprPtr expr); 31 | 32 | bool verify_with_z3(Z3Context &ctx, ExprPtr pre_cond, ExprPtr target, 33 | bool do_fork = false); 34 | 35 | void print_expr_z3(ExprPtr expr, std::ostream &os); 36 | } // namespace Symbolic 37 | -------------------------------------------------------------------------------- /verifier/src/abstract-functions.cc: -------------------------------------------------------------------------------- 1 | #include "abstract-functions.h" 2 | #include "data-structures.h" 3 | 4 | std::unique_ptr SymExecFunctions::instance = nullptr; 5 | 6 | std::string log_write_cnt_name = "__special_countet_num_log_write"; 7 | 8 | SymExecFunctions *SymExecFunctions::get() { 9 | if (instance == nullptr) { 10 | instance = std::make_unique(); 11 | } 12 | return instance.get(); 13 | } 14 | 15 | void SymExecFunctions::add_readonly_function(const std::string &func_name) { 16 | readonly_funcs_.insert(func_name); 17 | } 18 | 19 | bool SymExecFunctions::is_abstract_function(const std::string &func_name) { 20 | if (readonly_funcs_.find(func_name) != readonly_funcs_.end()) { 21 | return true; 22 | } 23 | 24 | if (counting_mode) { 25 | if (is_prefix(func_name, "UndoLogDisk::") || 26 | is_prefix(func_name, "RedoLogDisk::")) { 27 | return true; 28 | } 29 | } 30 | if (is_prefix(func_name, "std::vector<")) { 31 | return true; 32 | } 33 | 34 | if (func_name == "sem_post" || func_name == "sem_destroy" || 35 | func_name == "sem_init") { 36 | return true; 37 | } 38 | return false; 39 | } 40 | 41 | StatePtrList SymExecFunctions::run_function( 42 | std::shared_ptr s, const std::string &fn, 43 | const std::string &dst_reg, const std::vector ¶ms) { 44 | std::string func_name = fn; 45 | if (readonly_funcs_.find(func_name) != readonly_funcs_.end()) { 46 | s->set_reg_val(dst_reg, 47 | RegValue{mk_bv_var(64, name_gen_(func_name + "_ret"))}); 48 | // auto shm_buf_name = s->shm_mem->name; 49 | // SymPointer ptr; 50 | // ptr.pointer_base = shm_buf_name; 51 | // ptr.offset = mk_bv_var(64, name_gen_(func_name)); 52 | // ptr.is_shm_ptr = true; 53 | // s->set_reg_val(dst_reg, RegValue{ptr}); 54 | s->inst_iter++; 55 | return {s}; 56 | } 57 | if (log_write_bounds.find(func_name) != log_write_bounds.end()) { 58 | auto bound = log_write_bounds.find(func_name)->second; 59 | auto cnt = s->find_pointee(log_write_cnt_name) 60 | ->load(mk_concrete_bv(64, 0), 8) 61 | .get_val(); 62 | auto delta = mk_bv_var(64, s->name_gen("new_cnt")); 63 | auto in_bound = mk_expr_ptr(UltExpr, {delta, mk_concrete_bv(64, bound)}); 64 | 65 | auto new_cnt = mk_expr_ptr(AddExpr, {cnt, delta}); 66 | s->find_pointee(log_write_cnt_name) 67 | ->store(mk_concrete_bv(64, 0), RegValue{new_cnt}); 68 | s->add_pre_cond(in_bound); 69 | 70 | // TODO: use symbolic states for all memory objects 71 | // 72 | s->inst_iter++; 73 | return {s}; 74 | } 75 | 76 | if (is_prefix(func_name, "UndoLogDisk::") || 77 | is_prefix(func_name, "RedoLogDisk::")) { 78 | auto pos = func_name.find("::"); 79 | pos += 2; 80 | auto method_name = func_name.substr(pos); 81 | if (is_prefix(method_name, "Write(")) { 82 | auto cnt = s->find_pointee(log_write_cnt_name) 83 | ->load(mk_concrete_bv(64, 0), 8) 84 | .get_val(); 85 | auto new_cnt = mk_expr_ptr(AddExpr, {cnt, mk_concrete_bv(64, 1)}); 86 | s->find_pointee(log_write_cnt_name) 87 | ->store(mk_concrete_bv(64, 0), RegValue{new_cnt}); 88 | s->inst_iter++; 89 | return {s}; 90 | } 91 | } 92 | 93 | if (func_name == "sem_post" || func_name == "sem_destroy" || 94 | func_name == "sem_init") { 95 | s->set_reg_val(dst_reg, RegValue{mk_concrete_bv(32, 0)}); 96 | s->inst_iter++; 97 | return {s}; 98 | } 99 | 100 | if (is_prefix(func_name, "std::vector<")) { 101 | auto vec_ptr = params[0].get_ptr(); 102 | auto obj = s->find_pointee(vec_ptr.pointer_base); 103 | assert(!vec_ptr.offset->simplify()->is_symbolic()); 104 | assert(std::dynamic_pointer_cast( 105 | vec_ptr.offset->simplify()) 106 | ->get_val() == 0); 107 | assert(obj->type() == PointeeType::Vector); 108 | auto vec = std::dynamic_pointer_cast(obj); 109 | func_name = func_name.substr(std::string("std::").length()); 110 | auto pos = func_name.find(">::"); 111 | if (pos != std::string::npos) { 112 | auto method_name = func_name.substr(pos + std::string(">::").length()); 113 | if (method_name == "size() const") { 114 | s->set_reg_val(dst_reg, RegValue{vec->n_elements}); 115 | s->inst_iter++; 116 | return {s}; 117 | } else if (is_prefix(method_name, "operator[]")) { 118 | auto val = vec->get(params[1].get_val()); 119 | auto val_size = vec->val_type->get_bv_width(); 120 | auto result_buf = 121 | std::make_shared(s->name_gen("vec_result"), val_size / 8); 122 | s->add_pointee(result_buf); 123 | result_buf->store_be(mk_concrete_bv(64, 0), RegValue{val}); 124 | s->set_reg_val(dst_reg, RegValue{SymPointer{result_buf->name}}); 125 | s->inst_iter++; 126 | return {s}; 127 | } 128 | } 129 | } 130 | assert(false && "unknown function"); 131 | } 132 | -------------------------------------------------------------------------------- /verifier/src/data-structures.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "data-structures.h" 5 | #include "z3-gen.h" 6 | 7 | using namespace Symbolic; 8 | 9 | std::ostream &operator<<(std::ostream &os, const PointeeType &t) { 10 | switch (t) { 11 | case PointeeType::PointerStore: 12 | os << "PointerStore"; 13 | break; 14 | case PointeeType::Vector: 15 | os << "Vector"; 16 | break; 17 | case PointeeType::HashMap: 18 | os << "HashMap"; 19 | break; 20 | case PointeeType::Buffer: 21 | os << "Buffer"; 22 | break; 23 | case PointeeType::Packet: 24 | os << "Packet"; 25 | break; 26 | case PointeeType::Object: 27 | os << "Object"; 28 | break; 29 | case PointeeType::Invalid: 30 | os << "Invalid"; 31 | break; 32 | } 33 | return os; 34 | } 35 | 36 | RegValue PointerStore::load(Symbolic::ExprPtr off, uint64_t size) const { 37 | throw "PointerStore: load not implemented"; 38 | } 39 | 40 | void PointerStore::store(Symbolic::ExprPtr off, RegValue val) { 41 | throw "PointerStore: store not implemented"; 42 | } 43 | 44 | PointerStore::MultiPtr PointerStore::load_ptr(Symbolic::ExprPtr off) const { 45 | MultiPtr result; 46 | uint64_t concrete_off = 0; 47 | const uint64_t ptr_size = 8; 48 | for (int i = 0; i < ptrs.size(); i++) { 49 | auto match = mk_expr_ptr(EqExpr, {mk_concrete_bv(64, concrete_off), off}); 50 | 51 | MultiPtrEntry e; 52 | e.pre_cond = match; 53 | e.ptr = ptrs[i]; 54 | result.push_back(e); 55 | 56 | concrete_off += ptr_size; 57 | } 58 | return result; 59 | } 60 | 61 | void PointerStore::print(std::ostream &os) const { 62 | os << "(PointerStore " << name << ")"; 63 | } 64 | 65 | Inaccessible::Inaccessible(const std::string &n) { name = n; } 66 | 67 | void Inaccessible::print(std::ostream &os) const { 68 | os << "(Inaccessible " << name << ")"; 69 | } 70 | 71 | Buffer::Buffer(const std::string &name) { 72 | auto bv_64 = std::make_shared(64); 73 | auto bv_8 = std::make_shared(8); 74 | std::vector> kt_l = {bv_64}; 75 | auto ft = std::make_shared(kt_l, bv_8); 76 | auto uf = std::make_shared(ft, name); 77 | Lambda::FuncT func = [uf](const OpApplyNode::ArgList &args) -> ExprPtr { 78 | return mk_expr_ptr(FuncApply, uf, args); 79 | }; 80 | content_f = std::make_shared(ft, func); 81 | sized = false; 82 | size = 0; 83 | this->name = name; 84 | } 85 | 86 | Buffer::Buffer(const std::string &name, int sz, int _cell_size) { 87 | auto bv_64 = std::make_shared(64); 88 | auto bv_val = std::make_shared(8 * _cell_size); 89 | std::vector> kt_l = {bv_64}; 90 | auto ft = std::make_shared(kt_l, bv_val); 91 | auto uf = std::make_shared(ft, name); 92 | Lambda::FuncT func = [uf](const OpApplyNode::ArgList &args) -> ExprPtr { 93 | return mk_expr_ptr(FuncApply, uf, args); 94 | }; 95 | content_f = std::make_shared(ft, func); 96 | if (sz > 0) { 97 | sized = true; 98 | size = sz; 99 | } else { 100 | sized = false; 101 | size = 0; 102 | } 103 | cell_size = _cell_size; 104 | this->name = name; 105 | } 106 | 107 | RegValue Buffer::load_be(ExprPtr off, uint64_t size) const { 108 | assert(cell_size == 1); 109 | int num_loads = size; 110 | std::vector bytes; 111 | ExprPtr ptr = off; 112 | for (int i = 0; i < num_loads; i++) { 113 | auto b = mk_expr_ptr(FuncApply, content_f, {ptr}); 114 | ptr = mk_expr_ptr(AddExpr, {ptr, mk_expr_ptr(ConcreteBv, 64, 1)}); 115 | bytes.push_back(b); 116 | } 117 | return RegValue{mk_expr_ptr(ConcatExpr, bytes)}; 118 | } 119 | 120 | void Buffer::store_be(ExprPtr off, RegValue val) { 121 | assert(cell_size == 1); 122 | assert(val.is_val()); 123 | auto bv = val.get_val(); 124 | auto num_bytes = bv->type->get_bv_width() / 8; 125 | assert(bv->type->get_bv_width() % 8 == 0); 126 | std::vector bytes; 127 | for (int i = num_bytes - 1; i >= 0; i--) { 128 | auto b = mk_expr_ptr(ExtractExpr, bv, i * 8, (i + 1) * 8); 129 | bytes.push_back(b); 130 | } 131 | auto old_f = content_f; 132 | Lambda::FuncT func = [off, old_f, num_bytes, 133 | bytes](const OpApplyNode::ArgList &args) -> ExprPtr { 134 | auto result = old_f->func(args); 135 | for (int i = 0; i < num_bytes; i++) { 136 | auto off_i = mk_expr_ptr(AddExpr, {off, mk_expr_ptr(ConcreteBv, 64, i)}); 137 | auto eq = mk_expr_ptr(EqExpr, {args[0], off_i}); 138 | result = mk_expr_ptr(IteExpr, eq, bytes[i], result); 139 | } 140 | return result; 141 | }; 142 | content_f = std::make_shared(content_f->type, func); 143 | modified = true; 144 | } 145 | 146 | RegValue Buffer::load(ExprPtr off, uint64_t size) const { 147 | if (cell_size > 1) { 148 | assert(size == cell_size); 149 | auto val = mk_expr_ptr(FuncApply, content_f, {off}); 150 | return RegValue{val}; 151 | } 152 | int num_bytes = size; 153 | std::vector bytes; 154 | ExprPtr ptr = off; 155 | for (int i = 0; i < num_bytes; i++) { 156 | auto b = mk_expr_ptr(FuncApply, content_f, {ptr}); 157 | ptr = mk_expr_ptr(AddExpr, {ptr, mk_expr_ptr(ConcreteBv, 64, 1)}); 158 | bytes.push_back(b); 159 | } 160 | std::reverse(bytes.begin(), bytes.end()); 161 | return RegValue{mk_expr_ptr(ConcatExpr, bytes)}; 162 | } 163 | 164 | void Buffer::store(ExprPtr off, RegValue val) { 165 | assert(val.is_val()); 166 | auto bv = val.get_val(); 167 | auto num_bytes = bv->type->get_bv_width() / 8; 168 | if (cell_size > 1) { 169 | assert(num_bytes == cell_size); 170 | auto old_f = content_f; 171 | Lambda::FuncT func = [off, old_f, 172 | bv](const OpApplyNode::ArgList &args) -> ExprPtr { 173 | auto result = old_f->func(args); 174 | auto eq = mk_expr_ptr(EqExpr, {args[0], off}); 175 | result = mk_expr_ptr(IteExpr, eq, bv, result); 176 | return result; 177 | }; 178 | content_f = std::make_shared(content_f->type, func); 179 | return; 180 | } 181 | assert(bv->type->get_bv_width() % 8 == 0); 182 | std::vector bytes; 183 | for (int i = 0; i < num_bytes; i++) { 184 | auto b = mk_expr_ptr(ExtractExpr, bv, i * 8, (i + 1) * 8); 185 | bytes.push_back(b); 186 | } 187 | auto old_f = content_f; 188 | Lambda::FuncT func = [off, old_f, num_bytes, 189 | bytes](const OpApplyNode::ArgList &args) -> ExprPtr { 190 | auto result = old_f->func(args); 191 | for (int i = 0; i < num_bytes; i++) { 192 | auto off_i = mk_expr_ptr(AddExpr, {off, mk_expr_ptr(ConcreteBv, 64, i)}); 193 | auto eq = mk_expr_ptr(EqExpr, {args[0], off_i}); 194 | result = mk_expr_ptr(IteExpr, eq, bytes[i], result); 195 | } 196 | return result; 197 | }; 198 | content_f = std::make_shared(content_f->type, func); 199 | modified = true; 200 | } 201 | 202 | void Buffer::print(std::ostream &os) const { os << "(Buffer " << name << ")"; } 203 | Symbolic::ExprPtr Buffer::equals(Buffer &other) const { return nullptr; } 204 | 205 | ConcreteCacheBuffer::ConcreteCacheBuffer(const std::string &name) 206 | : Buffer(name) {} 207 | ConcreteCacheBuffer::ConcreteCacheBuffer(const std::string &name, int size) 208 | : Buffer(name, size) {} 209 | 210 | RegValue ConcreteCacheBuffer::load(Symbolic::ExprPtr off, uint64_t size) const { 211 | off = off->simplify(); 212 | if (!off->is_symbolic()) { 213 | auto off_val = 214 | std::dynamic_pointer_cast(off)->get_val(); 215 | for (auto &r : cache) { 216 | if (r.off == off_val && r.size == size) { 217 | return r.val; 218 | } 219 | } 220 | } 221 | return Buffer::load(off, size); 222 | } 223 | 224 | void ConcreteCacheBuffer::store(Symbolic::ExprPtr off, RegValue val) { 225 | off = off->simplify(); 226 | if (!off->is_symbolic() && val.is_val()) { 227 | auto off_val = 228 | std::dynamic_pointer_cast(off)->get_val(); 229 | 230 | std::vector new_cache; 231 | auto val_num_bits = val.get_val()->type->get_bv_width(); 232 | assert(val_num_bits % 8 == 0); 233 | for (auto &r : cache) { 234 | if (off_val >= r.off + r.size || off_val + val_num_bits / 8 <= r.off) { 235 | new_cache.push_back(r); 236 | } 237 | } 238 | cache = new_cache; 239 | 240 | CacheRegion r; 241 | r.off = off_val; 242 | r.size = val_num_bits / 8; 243 | r.val = val; 244 | cache.push_back(r); 245 | } else { 246 | cache.clear(); 247 | } 248 | return Buffer::store(off, val); 249 | } 250 | 251 | RegValue ConcreteCacheBuffer::load_be(Symbolic::ExprPtr off, 252 | uint64_t size) const { 253 | off = off->simplify(); 254 | if (!off->is_symbolic()) { 255 | auto off_val = 256 | std::dynamic_pointer_cast(off)->get_val(); 257 | for (auto &r : cache) { 258 | if (r.off == off_val && r.size == size) { 259 | if (r.val.is_ptr()) { 260 | break; 261 | } 262 | return RegValue{endian_reverse(r.val.get_val())}; 263 | } 264 | } 265 | } 266 | return Buffer::load(off, size); 267 | } 268 | 269 | void ConcreteCacheBuffer::store_be(Symbolic::ExprPtr off, RegValue val) { 270 | off = off->simplify(); 271 | if (!off->is_symbolic() && val.is_val()) { 272 | auto off_val = 273 | std::dynamic_pointer_cast(off)->get_val(); 274 | 275 | std::vector new_cache; 276 | auto val_num_bits = val.get_val()->type->get_bv_width(); 277 | assert(val_num_bits % 8 == 0); 278 | for (auto &r : cache) { 279 | if (off_val >= r.off + r.size || off_val + val_num_bits / 8 <= r.off) { 280 | new_cache.push_back(r); 281 | } 282 | } 283 | cache = new_cache; 284 | 285 | CacheRegion r; 286 | r.off = off_val; 287 | r.size = val_num_bits / 8; 288 | r.val = RegValue{endian_reverse(val.get_val())}; 289 | cache.push_back(r); 290 | } else { 291 | std::cout << "dropping cache..." << std::endl; 292 | cache.clear(); 293 | } 294 | return Buffer::store_be(off, val); 295 | } 296 | 297 | void ConcreteCacheBuffer::print(std::ostream &os) const { 298 | os << "(ConcreteCacheBuffer " << name << ")"; 299 | } 300 | 301 | AbstractVector::AbstractVector(const std::string &name, 302 | std::shared_ptr ele_type) 303 | : AbstractVector(name, ele_type, 304 | mk_expr_ptr(SymbolicVar, std::make_shared(64), 305 | name + "!len")) {} 306 | 307 | AbstractVector::AbstractVector(const std::string &name, 308 | std::shared_ptr ele_type, 309 | uint64_t n) 310 | : AbstractVector(name, ele_type, mk_expr_ptr(ConcreteBv, 64, n)) {} 311 | 312 | AbstractVector::AbstractVector(const std::string &name, 313 | std::shared_ptr ele_type, 314 | Symbolic::ExprPtr n) { 315 | auto bv_64 = std::make_shared(64); 316 | std::vector> kt_l = {bv_64}; 317 | auto e_type = std::dynamic_pointer_cast(ele_type); 318 | auto ft = std::make_shared(kt_l, e_type); 319 | auto uf = std::make_shared(ft, name); 320 | Lambda::FuncT func = [uf](const OpApplyNode::ArgList &args) -> ExprPtr { 321 | return mk_expr_ptr(FuncApply, uf, args); 322 | }; 323 | arr_f = std::make_shared(ft, func); 324 | n_elements = n; 325 | this->name = name; 326 | val_type = ele_type; 327 | } 328 | 329 | void AbstractVector::print(std::ostream &os) const { 330 | os << "(vector " << name << ")"; 331 | } 332 | 333 | RegValue AbstractVector::handle_req(const std::string &method_name, 334 | const std::vector &args, 335 | std::shared_ptr ctx) { 336 | if (method_name == "get") { 337 | } 338 | return RegValue{nullptr}; 339 | } 340 | 341 | bool AbstractVector::bound_check(Symbolic::ExprPtr idx) const { 342 | Z3Context ctx; 343 | auto lb = mk_expr_ptr(UleExpr, {mk_expr_ptr(ConcreteBv, 64, 0), idx}); 344 | auto up = mk_expr_ptr(UltExpr, {idx, n_elements}); 345 | auto bound = mk_expr_ptr(LAndExpr, {lb, up}); 346 | auto expr = gen_z3_expr(ctx, bound).get_expr(); 347 | z3::solver sol(ctx.ctx); 348 | sol.add(!expr); 349 | return sol.check() == z3::unsat; 350 | } 351 | 352 | Symbolic::ExprPtr AbstractVector::get(Symbolic::ExprPtr idx) const { 353 | using namespace Symbolic; 354 | assert(idx->type->is_bv_type() && idx->type->get_bv_width() == 64); 355 | return mk_expr_ptr(FuncApply, arr_f, {idx}); 356 | } 357 | 358 | void AbstractVector::set(Symbolic::ExprPtr idx, Symbolic::ExprPtr val) { 359 | using namespace Symbolic; 360 | assert(idx->type->is_bv_type() && idx->type->get_bv_width() == 64); 361 | assert(val->type->equal_to(val_type)); 362 | std::shared_ptr old_arr_f = arr_f; 363 | Lambda::FuncT func = [old_arr_f, val, 364 | idx](const OpApplyNode::ArgList &args) -> ExprPtr { 365 | auto old_val = mk_expr_ptr(FuncApply, old_arr_f, args); 366 | auto eq = mk_expr_ptr(EqExpr, {idx, args[0]}); 367 | return mk_expr_ptr(IteExpr, eq, val, old_val); 368 | }; 369 | arr_f = std::make_shared(arr_f->type, func); 370 | } 371 | 372 | void AbstractVector::push_back(Symbolic::ExprPtr val) { 373 | using namespace Symbolic; 374 | assert(val->type->equal_to(val_type)); 375 | auto idx = n_elements; 376 | n_elements = 377 | mk_expr_ptr(AddExpr, {n_elements, mk_expr_ptr(ConcreteBv, 64, 1)}); 378 | modified = true; 379 | set(idx, val); 380 | } 381 | 382 | AbstractMap::AbstractMap(const std::string &name, 383 | const Symbolic::PtrList &key_types, 384 | const Symbolic::PtrList &val_types) { 385 | using namespace Symbolic; 386 | std::vector> key_type_list; 387 | for (auto t : key_types) { 388 | assert(t->is_val()); 389 | key_type_list.push_back(std::dynamic_pointer_cast(t)); 390 | } 391 | auto bv_1 = 392 | std::dynamic_pointer_cast(std::make_shared(1)); 393 | std::vector> val_type_list; 394 | for (auto t : val_types) { 395 | assert(t->is_val()); 396 | val_type_list.push_back(std::dynamic_pointer_cast(t)); 397 | } 398 | 399 | auto ft = std::make_shared(key_type_list, bv_1); 400 | auto uf = mk_expr_ptr(SymbolicVar, ft, name + "!contains"); 401 | Lambda::FuncT func = [uf](const OpApplyNode::ArgList &args) -> ExprPtr { 402 | return mk_expr_ptr(FuncApply, uf, args); 403 | }; 404 | contains_f = std::make_shared(ft, func); 405 | 406 | for (int i = 0; i < val_type_list.size(); i++) { 407 | auto ft = std::make_shared(key_type_list, val_type_list[i]); 408 | auto uf = mk_expr_ptr(SymbolicVar, ft, name + "!val!" + std::to_string(i)); 409 | Lambda::FuncT func = [uf](const OpApplyNode::ArgList &args) -> ExprPtr { 410 | return mk_expr_ptr(FuncApply, uf, args); 411 | }; 412 | val_f.push_back(std::make_shared(ft, func)); 413 | } 414 | this->name = name; 415 | this->key_types = key_types; 416 | this->val_types = val_types; 417 | } 418 | 419 | RegValue AbstractMap::handle_req(const std::string &method_name, 420 | const std::vector &args, 421 | std::shared_ptr ctx) { 422 | if (method_name == "get") { 423 | } 424 | return RegValue{nullptr}; 425 | } 426 | 427 | Symbolic::ExprPtr 428 | AbstractMap::contains(const Symbolic::OpApplyNode::ArgList &args) const { 429 | return mk_expr_ptr(FuncApply, contains_f, args); 430 | } 431 | 432 | std::vector 433 | AbstractMap::get_vals(const Symbolic::OpApplyNode::ArgList &args) const { 434 | std::vector result; 435 | for (int i = 0; i < val_f.size(); i++) { 436 | result.push_back(mk_expr_ptr(FuncApply, val_f[i], args)); 437 | } 438 | return result; 439 | } 440 | 441 | void AbstractMap::set_vals(const std::vector &args, 442 | const std::vector &vals) { 443 | using namespace Symbolic; 444 | std::shared_ptr old_f = contains_f; 445 | std::vector keys = args; 446 | Lambda::FuncT func = [old_f, keys](const std::vector &a) -> ExprPtr { 447 | auto old_result = mk_expr_ptr(FuncApply, old_f, a); 448 | ExprPtr eq = nullptr; 449 | for (int i = 0; i < keys.size(); i++) { 450 | auto c = mk_expr_ptr(EqExpr, {keys[i], a[i]}); 451 | if (eq == nullptr) { 452 | eq = c; 453 | } else { 454 | eq = mk_expr_ptr(LAndExpr, {eq, c}); 455 | } 456 | } 457 | return mk_expr_ptr(IteExpr, eq, mk_expr_ptr(ConcreteBv, 1, 1), old_result); 458 | }; 459 | contains_f = std::make_shared(old_f->type, func); 460 | 461 | for (int i = 0; i < vals.size(); i++) { 462 | ExprPtr v = vals[i]; 463 | old_f = val_f[i]; 464 | func = [old_f, v, keys](const std::vector &a) -> ExprPtr { 465 | auto old_result = mk_expr_ptr(FuncApply, old_f, a); 466 | ExprPtr eq = nullptr; 467 | for (int i = 0; i < keys.size(); i++) { 468 | auto c = mk_expr_ptr(EqExpr, {keys[i], a[i]}); 469 | if (eq == nullptr) { 470 | eq = c; 471 | } else { 472 | eq = mk_expr_ptr(LAndExpr, {eq, c}); 473 | } 474 | } 475 | return mk_expr_ptr(IteExpr, eq, v, old_result); 476 | }; 477 | val_f[i] = std::make_shared(old_f->type, func); 478 | } 479 | modified = true; 480 | } 481 | 482 | void AbstractMap::delete_val(const Symbolic::OpApplyNode::ArgList &args) { 483 | using namespace Symbolic; 484 | std::shared_ptr old_f = contains_f; 485 | std::vector keys = args; 486 | Lambda::FuncT func = [old_f, keys](const std::vector &a) -> ExprPtr { 487 | auto old_result = mk_expr_ptr(FuncApply, old_f, a); 488 | ExprPtr eq = nullptr; 489 | for (int i = 0; i < keys.size(); i++) { 490 | auto c = mk_expr_ptr(EqExpr, {keys[i], a[i]}); 491 | if (eq == nullptr) { 492 | eq = c; 493 | } else { 494 | eq = mk_expr_ptr(LAndExpr, {eq, c}); 495 | } 496 | } 497 | return mk_expr_ptr(IteExpr, eq, mk_expr_ptr(ConcreteBv, 1, 0), old_result); 498 | }; 499 | contains_f = std::make_shared(old_f->type, func); 500 | modified = true; 501 | } 502 | 503 | void AbstractMap::print(std::ostream &os) const { 504 | os << "(abstract-map " << name << ")"; 505 | } 506 | 507 | Packet::Packet(const std::string &n, std::shared_ptr state) { 508 | name = n; 509 | content_buf_name = name + "!content"; 510 | auto bv32 = std::make_shared(32); 511 | len = mk_expr_ptr(SymbolicVar, bv32, name + "!len"); 512 | 513 | auto pkt_content = std::make_shared(content_buf_name, 1600); 514 | state->objects.insert({content_buf_name, pkt_content}); 515 | 516 | anno_buf_name = name + "!anno"; 517 | auto anno_buf = std::make_shared(anno_buf_name, 128); 518 | state->objects.insert({anno_buf_name, anno_buf}); 519 | } 520 | 521 | void Packet::print(std::ostream &os) const { os << "(Packet " << name << ")"; } 522 | 523 | RegValue Packet::handle_req(const std::string &method_name, 524 | const std::vector &args, 525 | std::shared_ptr ctx) { 526 | assert(false && "unknown req"); 527 | } 528 | 529 | AbstractObject::AbstractObject(const std::string &n) { name = n; } 530 | 531 | RegValue AbstractObject::handle_req(const std::string &method_name, 532 | const std::vector &args, 533 | std::shared_ptr ctx) { 534 | throw "AbstractObject: handle_req not implemented"; 535 | } 536 | 537 | std::shared_ptr AbstractObject::copy_self() const { 538 | auto result = std::make_shared(*this); 539 | return result; 540 | } 541 | 542 | void AbstractObject::print(std::ostream &os) const { 543 | os << "(abstract-object " << name << ")"; 544 | } 545 | 546 | int AbstractObject::find_region(uint64_t off, Region &result) const { 547 | for (auto &r : regions) { 548 | if (off >= r.start_off && off < r.start_off + r.size) { 549 | result = r; 550 | return 0; 551 | } 552 | } 553 | return -1; 554 | } 555 | 556 | int AbstractObject::find_region(ExprPtr off, Region &r, 557 | ExprPtr pre_cond) const { 558 | // TODO: fill in this 559 | return -1; 560 | } 561 | 562 | std::vector 563 | AbstractObject::find_region(Symbolic::ExprPtr off) const { 564 | std::vector result; 565 | for (auto &r : regions) { 566 | auto lb = mk_expr_ptr(UleExpr, {mk_concrete_bv(64, r.start_off), off}); 567 | auto ub = 568 | mk_expr_ptr(UltExpr, {off, mk_concrete_bv(64, r.start_off + r.size)}); 569 | auto in_range = mk_expr_ptr(AndExpr, {lb, ub}); 570 | 571 | FindResultEntry e; 572 | e.pre_cond = in_range; 573 | e.region = r; 574 | result.push_back(e); 575 | } 576 | return result; 577 | } 578 | 579 | void AbstractObject::add_region(const Region &new_region) { 580 | // first make sure that there is no overlap 581 | auto r_start = new_region.start_off; 582 | auto r_end = r_start + new_region.size; 583 | 584 | for (auto &r : regions) { 585 | auto end = r.start_off + r.size; 586 | if (r_start < end && r.start_off < r_end) { 587 | assert(false && "inserting overlapping region"); 588 | } 589 | } 590 | 591 | regions.push_back(new_region); 592 | } 593 | 594 | void AbstractObject::add_ptr_at(std::shared_ptr s, 595 | const SymPointer &ptr, uint64_t off) { 596 | auto ptr_store = std::make_shared( 597 | s->name_gen(ptr.pointer_base + "_ptr_store"), ptr); 598 | 599 | s->add_pointee(ptr_store); 600 | 601 | Region r; 602 | r.type = Region::T::INLINED; 603 | r.start_off = off; 604 | r.size = 8; 605 | r.ptr = SymPointer(ptr_store->name); 606 | 607 | add_region(r); 608 | } 609 | 610 | void AbstractObject::add_obj_ptr_at(std::shared_ptr s, 611 | std::shared_ptr obj, 612 | uint64_t off) { 613 | SymPointer obj_ptr(obj->name); 614 | add_ptr_at(s, obj_ptr, off); 615 | } 616 | -------------------------------------------------------------------------------- /verifier/src/llvm-helpers.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "llvm-helpers.h" 8 | #include "utils.h" 9 | 10 | std::vector find_element_entry(llvm::Module *module, 11 | const std::string &element_name) { 12 | std::vector patterns = { 13 | element_name + "::push(", 14 | element_name + "::pull(", 15 | element_name + "::simple_action(", 16 | }; 17 | 18 | std::unordered_map matched; 19 | 20 | for (auto iter = module->begin(); iter != module->end(); iter++) { 21 | auto func_name = iter->getName().str(); 22 | size_t size = 0; 23 | int status = 0; 24 | char *n = abi::__cxa_demangle(func_name.c_str(), NULL, &size, &status); 25 | if (n != NULL) { 26 | std::string s(n); 27 | for (auto &pat : patterns) { 28 | if (is_prefix(s, pat)) { 29 | assert(matched.find(pat) == matched.end()); 30 | matched[pat] = func_name; 31 | } 32 | } 33 | } 34 | } 35 | 36 | std::vector vec; 37 | for (auto iter = matched.begin(); iter != matched.end(); iter++) { 38 | vec.push_back(iter->second); 39 | } 40 | 41 | return vec; 42 | } 43 | 44 | std::string get_type_name(llvm::Type *t) { 45 | std::string res; 46 | llvm::raw_string_ostream stream{res}; 47 | t->print(stream); 48 | stream.str(); 49 | 50 | // we only keep things before the first space 51 | auto found = res.find(' '); 52 | if (found != std::string::npos) { 53 | res = res.substr(0, found); 54 | } 55 | return res; 56 | } 57 | 58 | std::string llvm_type_to_str(llvm::Type *t) { 59 | std::string res; 60 | llvm::raw_string_ostream stream{res}; 61 | t->print(stream); 62 | stream.str(); 63 | return res; 64 | } 65 | 66 | uint64_t get_type_size(const llvm::Module *m, llvm::Type *type) { 67 | llvm::DataLayout *dl = new llvm::DataLayout(m); 68 | uint64_t type_size = dl->getTypeStoreSize(type); 69 | return type_size; 70 | } 71 | 72 | std::string get_name(const llvm::Value &value) { 73 | std::string res; 74 | llvm::raw_string_ostream stream{res}; 75 | value.printAsOperand(stream, false); 76 | stream.str(); 77 | return res; 78 | } 79 | 80 | int64_t get_int_val(const llvm::Value *value) { 81 | if (const llvm::ConstantInt *CI = llvm::dyn_cast(value)) { 82 | if (CI->getBitWidth() <= 64) { 83 | return CI->getSExtValue(); 84 | } 85 | } 86 | assert(false && "not an integer constant"); 87 | } 88 | 89 | std::string demangle_cpp_name(const std::string &cpp_name) { 90 | size_t size = 0; 91 | int status = 0; 92 | char *n = abi::__cxa_demangle(cpp_name.c_str(), NULL, &size, &status); 93 | if (n == NULL) { 94 | return ""; 95 | } else { 96 | std::string s = n; 97 | free(n); 98 | return s; 99 | } 100 | } 101 | 102 | std::string trim(const std::string &s) { 103 | if (s == "") { 104 | return s; 105 | } 106 | auto start = s.find_first_not_of(" "); 107 | auto end = s.find_last_not_of(" "); 108 | return s.substr(start, end - start + 1); 109 | } 110 | 111 | std::vector split_template(const std::string &id) { 112 | std::vector type_args; 113 | std::string filtered; 114 | size_t curr_pos = 0; 115 | while (1) { 116 | // find the first '<' 117 | auto l = id.find('<', curr_pos); 118 | if (l == std::string::npos) { 119 | filtered = filtered + id.substr(curr_pos); 120 | goto out; 121 | } 122 | auto r = id.find('>', curr_pos); 123 | if (r == std::string::npos) { 124 | assert(false && "mismatched < and >"); 125 | } 126 | size_t pos = l + 1; 127 | while (pos < r) { 128 | auto comma = id.find(',', pos); 129 | if (comma == std::string::npos || comma >= r) { 130 | type_args.push_back(id.substr(pos, r - pos)); 131 | break; 132 | } 133 | type_args.push_back(id.substr(pos, comma - pos)); 134 | pos = comma + 1; 135 | } 136 | filtered = filtered + id.substr(curr_pos, l - curr_pos); 137 | curr_pos = r + 1; 138 | } 139 | out: 140 | std::vector result; 141 | result.push_back(trim(filtered)); 142 | for (int i = 0; i < type_args.size(); i++) { 143 | result.push_back(trim(type_args[i])); 144 | } 145 | return result; 146 | } 147 | -------------------------------------------------------------------------------- /verifier/src/logwrite_bound.cc: -------------------------------------------------------------------------------- 1 | #include "logwrite_bound.h" 2 | #include "data-structures.h" 3 | 4 | std::unique_ptr BoundRegistry::instance = nullptr; 5 | 6 | BoundRegistry *BoundRegistry::get() { 7 | if (BoundRegistry::instance == nullptr) { 8 | BoundRegistry::instance = std::make_unique(); 9 | } 10 | return BoundRegistry::instance.get(); 11 | } 12 | 13 | bool BoundRegistry::have_record(const std::string &fn) const { 14 | return bounds_.find(fn) != bounds_.end(); 15 | } 16 | 17 | int BoundRegistry::find_bound(const std::string &fn, 18 | std::shared_ptr &s, 19 | const std::vector ¶ms, 20 | LogWriteBound &bound) { 21 | auto iter = bounds_.find(fn); 22 | if (iter == bounds_.end()) { 23 | // try again after demangle 24 | auto demangled = demangle_cpp_name(fn); 25 | iter = bounds_.find(demangled); 26 | } 27 | if (iter == bounds_.end()) { 28 | return -1; 29 | } 30 | 31 | auto &b = iter->second; 32 | auto pre_cond = b.pre_cond(params); 33 | 34 | Symbolic::Z3Context ctx; 35 | int verified = verify_with_z3(ctx, nullptr, pre_cond); 36 | if (!verified) { 37 | return -1; 38 | } 39 | 40 | // reset the entire shm_mem 41 | // auto shm_buf_name = s->shm_mem->name; 42 | // auto new_shm = std::make_shared(shm_buf_name); 43 | // s->shm_mem = new_shm; 44 | // s->objects[shm_buf_name] = new_shm; 45 | bound = b; 46 | return b.bound; 47 | } 48 | 49 | void BoundRegistry::add_bound(const std::string &fn, 50 | const LogWriteBound &bound) { 51 | assert(bounds_.find(fn) == bounds_.end()); 52 | bounds_.insert({fn, bound}); 53 | } 54 | 55 | void verify_num_logwrite_bound(LogOpCounter *verifier, 56 | std::shared_ptr init_state, 57 | int bound) { 58 | auto cnt = std::make_shared(log_write_cnt_name); 59 | init_state->add_pointee(cnt); 60 | cnt->store(mk_concrete_bv(64, 0), RegValue{mk_concrete_bv(64, 0)}); 61 | 62 | auto states = verifier->run(init_state); 63 | 64 | Symbolic::Z3Context ctx; 65 | for (auto &s : states) { 66 | auto cnt = s->find_pointee(log_write_cnt_name); 67 | auto cnt_val = cnt->load(mk_concrete_bv(64, 0), 8).get_val(); 68 | auto goal = mk_expr_ptr(UltExpr, {cnt_val, mk_concrete_bv(64, bound)}); 69 | 70 | int verified = verify_with_z3(ctx, s->get_pre_cond(), goal); 71 | if (!verified) { 72 | std::cerr << "not ok" << std::endl; 73 | assert(false); 74 | } 75 | } 76 | } 77 | 78 | void verify_num_logwrite_bound(LogOpCounter *verifier, 79 | std::shared_ptr init_state, 80 | const std::vector ¶ms, 81 | const LogWriteBound &bound) { 82 | auto cnt = std::make_shared(log_write_cnt_name); 83 | init_state->add_pointee(cnt); 84 | for (int i = 0; i < params.size(); i++) { 85 | auto reg_name = "%" + std::to_string(i); 86 | init_state->set_reg_val(reg_name, params[i]); 87 | } 88 | cnt->store(mk_concrete_bv(64, 0), RegValue{mk_concrete_bv(64, 0)}); 89 | 90 | init_state->add_pre_cond(bound.pre_cond(params)); 91 | 92 | auto states = verifier->run(init_state); 93 | 94 | Symbolic::Z3Context ctx; 95 | for (int i = 0; i < states.size(); i++) { 96 | auto &s = states[i]; 97 | std::cout << "Verifying case " << i << "... \r" << std::flush; 98 | auto cnt = s->find_pointee(log_write_cnt_name); 99 | auto cnt_val = cnt->load(mk_concrete_bv(64, 0), 8).get_val(); 100 | auto goal = 101 | mk_expr_ptr(UleExpr, {cnt_val, mk_concrete_bv(64, bound.bound)}); 102 | 103 | int verified = verify_with_z3(ctx, s->get_pre_cond(), goal); 104 | if (!verified) { 105 | std::cerr << "\nnot ok " << std::endl; 106 | assert(false); 107 | } 108 | verified = verify_with_z3(ctx, s->get_pre_cond(), 109 | bound.post_cond(params, s->ret_val)); 110 | if (!verified) { 111 | std::cerr << "\npost cond not ok " << std::endl; 112 | assert(false); 113 | } 114 | } 115 | std::cout << std::endl; 116 | } 117 | 118 | void init_execution_state(std::shared_ptr &s) { 119 | auto client_obj = std::make_shared("client"); 120 | auto allocator_obj = std::make_shared("allocator_"); 121 | auto disk_obj = std::make_shared("log_disk_"); 122 | auto obj_log_obj = std::make_shared("object_log_"); 123 | 124 | s->add_pointee(client_obj); 125 | s->add_pointee(allocator_obj); 126 | s->add_pointee(disk_obj); 127 | s->add_pointee(obj_log_obj); 128 | 129 | client_obj->add_ptr_at(s, SymPointer{s->shm_mem->name}, 16); 130 | client_obj->add_obj_ptr_at(s, allocator_obj, 32); 131 | client_obj->add_ptr_at(s, SymPointer{s->shm_mem->name}, 40); 132 | client_obj->add_ptr_at(s, SymPointer{obj_log_obj->name}, 64); 133 | client_obj->add_ptr_at(s, SymPointer{disk_obj->name}, 80); 134 | 135 | SymPointer base_ptr{s->shm_mem->name}; 136 | base_ptr.is_shm_ptr = true; 137 | 138 | SymPointer free_list_ptr{s->shm_mem->name}; 139 | free_list_ptr.offset = mk_concrete_bv(64, 8); 140 | free_list_ptr.is_shm_ptr = true; 141 | 142 | allocator_obj->add_ptr_at(s, base_ptr, 0); 143 | allocator_obj->add_ptr_at(s, free_list_ptr, 8); 144 | allocator_obj->add_ptr_at(s, base_ptr, 16); 145 | allocator_obj->add_ptr_at(s, SymPointer{disk_obj->name}, 24); 146 | 147 | auto obj_log_content = 148 | std::make_shared(s->name_gen("obj_log_content")); 149 | auto obj_log_mem = std::make_shared("object_log_mem_"); 150 | s->add_pointee(obj_log_content); 151 | s->add_pointee(obj_log_mem); 152 | 153 | using Region = AbstractObject::Region; 154 | obj_log_obj->add_region( 155 | Region{Region::T::INLINED, 0, 12, SymPointer{obj_log_content->name}}); 156 | obj_log_obj->add_obj_ptr_at(s, obj_log_mem, 16); 157 | obj_log_obj->add_obj_ptr_at(s, disk_obj, 24); 158 | } 159 | -------------------------------------------------------------------------------- /verifier/src/symbolic-expr.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "symbolic-expr.h" 8 | 9 | namespace Symbolic { 10 | 11 | void ExprVisitor::visit(Expr &expr) { expr.accept(*this); } 12 | 13 | void BitVecType::print(std::ostream &os) const { 14 | os << "BitVecType(" << bitwidth << ")"; 15 | } 16 | 17 | bool BitVecType::equal_to(std::shared_ptr t) const { 18 | if (t->is_bv_type()) { 19 | auto w = t->get_bv_width(); 20 | return w == bitwidth; 21 | } 22 | 23 | return false; 24 | } 25 | 26 | void UFType::print(std::ostream &os) const { 27 | os << "Function("; 28 | for (int i = 0; i < key_types.size(); i++) { 29 | key_types[i]->print(os); 30 | os << " -> "; 31 | } 32 | val_type->print(os); 33 | os << ")"; 34 | } 35 | 36 | bool UFType::equal_to(std::shared_ptr t) const { 37 | if (t->is_func_type()) { 38 | auto ft = std::dynamic_pointer_cast(t); 39 | for (int i = 0; i < key_types.size(); i++) { 40 | if (!ft->key_types[i]->equal_to(key_types[i])) { 41 | return false; 42 | } 43 | } 44 | 45 | return val_type->equal_to(ft->val_type); 46 | } 47 | 48 | return false; 49 | } 50 | 51 | Expr::Expr() : uuid(boost::uuids::random_generator()()) {} 52 | 53 | OpApplyNode::OpApplyNode(const ArgList &args) : args_(args) { 54 | is_symbolic_ = false; 55 | for (auto &a : args) { 56 | if (a->is_symbolic()) { 57 | is_symbolic_ = true; 58 | break; 59 | } 60 | } 61 | is_symbolic_ = true; 62 | } 63 | 64 | void OpApplyNode::type_check(const PtrList &types) { 65 | if (types.size() != args_.size()) { 66 | std::stringstream ss; 67 | ss << "Arglist size mismatch: "; 68 | ss << "Expecting " << types.size() << " " 69 | << "got " << args_.size(); 70 | throw TypeCheckError{ss.str()}; 71 | } 72 | 73 | for (int i = 0; i < types.size(); i++) { 74 | if (!args_[i]->type->equal_to(types[i])) { 75 | std::stringstream ss; 76 | ss << "Arg #" << i << " : Type mismatch: "; 77 | ss << "Expecting "; 78 | types[i]->print(ss); 79 | ss << " " 80 | << "got "; 81 | args_[i]->type->print(ss); 82 | throw TypeCheckError{ss.str()}; 83 | } 84 | } 85 | } 86 | 87 | OpApplyNode::ArgList OpApplyNode::simplify_args() const { 88 | ArgList result; 89 | 90 | for (int i = 0; i < args_.size(); i++) { 91 | result.push_back(args_[i]->simplify()); 92 | } 93 | 94 | return result; 95 | } 96 | 97 | Lambda::Lambda(std::shared_ptr func_type, FuncT f) { 98 | if (!func_type->is_func_type()) { 99 | throw TypeCheckError{"Expecting function type"}; 100 | } 101 | std::shared_ptr ft = std::dynamic_pointer_cast(func_type); 102 | for (auto kt : ft->key_types) { 103 | if (!kt->is_val()) { 104 | throw TypeCheckError{"Function Args should be value type"}; 105 | } 106 | } 107 | if (!ft->val_type->is_val()) { 108 | throw TypeCheckError{"Function should return value type"}; 109 | } 110 | type = func_type; 111 | func = f; 112 | } 113 | 114 | Lambda::Lambda(const PtrList &arg_t, std::shared_ptr ret_t, 115 | FuncT f) { 116 | std::vector> arg_t_list; 117 | std::shared_ptr vt; 118 | for (auto t : arg_t) { 119 | if (!t->is_val()) { 120 | throw TypeCheckError{"Function Args should be value type"}; 121 | } 122 | arg_t_list.push_back(std::dynamic_pointer_cast(t)); 123 | } 124 | 125 | if (!ret_t->is_val()) { 126 | throw TypeCheckError{"Function return type shoud be value type "}; 127 | } else { 128 | vt = std::dynamic_pointer_cast(ret_t); 129 | } 130 | 131 | auto t = std::make_shared(arg_t_list, vt); 132 | 133 | type = t; 134 | func = f; 135 | } 136 | 137 | FuncApply::FuncApply(ExprPtr f, const ArgList &args) : OpApplyNode(args) { 138 | if (!f->type->is_func_type()) { 139 | throw TypeCheckError{"FuncApply expect function type"}; 140 | } 141 | auto ft = std::dynamic_pointer_cast(f->type); 142 | PtrList kt_list; 143 | for (auto t : ft->key_types) { 144 | kt_list.push_back(std::dynamic_pointer_cast(t)); 145 | } 146 | type_check(kt_list); 147 | type = ft->val_type; 148 | func = f; 149 | } 150 | 151 | BvBinOpExpr::BvBinOpExpr(const ArgList &args) : OpApplyNode(args) { 152 | if (args_.size() != 2) { 153 | throw TypeCheckError{"Bin op requires two args"}; 154 | } 155 | if (!args_[0]->type->equal_to(args_[1]->type)) { 156 | throw TypeCheckError{"Bin op args type mismatch"}; 157 | } 158 | if (!args_[0]->type->is_bv_type()) { 159 | throw TypeCheckError{"Expecting BitVec type"}; 160 | } 161 | auto t = std::make_shared(); 162 | t->bitwidth = args_[0]->type->get_bv_width(); 163 | type = std::dynamic_pointer_cast(t); 164 | } 165 | 166 | ExprPtr BvBinOpExpr::simplify() { 167 | auto args = simplify_args(); 168 | args_ = args; 169 | for (auto &a : args) { 170 | if (a->is_symbolic()) { 171 | return shared_from_this(); 172 | } 173 | } 174 | auto a1 = std::dynamic_pointer_cast(args[0])->get_val(); 175 | auto a2 = std::dynamic_pointer_cast(args[1])->get_val(); 176 | return this->concrete_binop(a1, a2); 177 | } 178 | 179 | BvBinPredExpr::BvBinPredExpr(const ArgList &args) : OpApplyNode(args) { 180 | if (args_.size() != 2) { 181 | throw TypeCheckError{"Bin op requires two args"}; 182 | } 183 | if (!args_[0]->type->equal_to(args_[1]->type)) { 184 | throw TypeCheckError{"Bin op args type mismatch"}; 185 | } 186 | if (!args_[0]->type->is_bv_type()) { 187 | throw TypeCheckError{"Expecting BitVec type"}; 188 | } 189 | auto t = std::make_shared(); 190 | t->bitwidth = 1; 191 | type = std::dynamic_pointer_cast(t); 192 | } 193 | 194 | ExprPtr BvBinPredExpr::simplify() { 195 | auto args = simplify_args(); 196 | args_ = args; 197 | for (auto &a : args) { 198 | if (a->is_symbolic()) { 199 | return shared_from_this(); 200 | } 201 | } 202 | auto a1 = std::dynamic_pointer_cast(args[0])->get_val(); 203 | auto a2 = std::dynamic_pointer_cast(args[1])->get_val(); 204 | return this->concrete_binop(a1, a2); 205 | } 206 | 207 | #define BV_BIN_OP_CONSTR(class_name) \ 208 | class_name::class_name(const ArgList &args) : BvBinOpExpr(args) {} 209 | 210 | #define BV_BIN_PRED_CONSTR(class_name) \ 211 | class_name::class_name(const ArgList &args) : BvBinPredExpr(args) {} 212 | 213 | #define BV_BIN_OP_CONCRETE(class_name, expr) \ 214 | ExprPtr class_name::concrete_binop(uint64_t a1, uint64_t a2) { \ 215 | uint64_t val = (uint64_t)(expr); \ 216 | auto bw = this->type->get_bv_width(); \ 217 | uint64_t mask = (1UL << bw) - 1; \ 218 | if (bw == 64) { \ 219 | mask = (uint64_t)-1L; \ 220 | } \ 221 | if (bw > 64) { \ 222 | return shared_from_this(); \ 223 | } \ 224 | val = val & mask; \ 225 | return mk_expr_ptr(ConcreteBv, this->type->get_bv_width(), val); \ 226 | } 227 | 228 | BV_BIN_OP_CONSTR(AddExpr); 229 | BV_BIN_OP_CONSTR(SubExpr); 230 | BV_BIN_OP_CONSTR(MulExpr); 231 | BV_BIN_OP_CONSTR(DivExpr); 232 | BV_BIN_OP_CONSTR(ModExpr); 233 | 234 | BV_BIN_OP_CONSTR(UDivExpr); 235 | BV_BIN_OP_CONSTR(UModExpr); 236 | 237 | BV_BIN_OP_CONSTR(LAndExpr); 238 | BV_BIN_OP_CONSTR(LOrExpr); 239 | BV_BIN_OP_CONSTR(LXorExpr); 240 | BV_BIN_OP_CONSTR(ImpliesExpr); 241 | 242 | BV_BIN_OP_CONSTR(AndExpr); 243 | BV_BIN_OP_CONSTR(OrExpr); 244 | BV_BIN_OP_CONSTR(XorExpr); 245 | BV_BIN_OP_CONSTR(LshExpr); 246 | BV_BIN_OP_CONSTR(LRshExpr); 247 | BV_BIN_OP_CONSTR(ARshExpr); 248 | 249 | BV_BIN_PRED_CONSTR(EqExpr); 250 | BV_BIN_PRED_CONSTR(NeqExpr); 251 | BV_BIN_PRED_CONSTR(LeExpr); 252 | BV_BIN_PRED_CONSTR(LtExpr); 253 | BV_BIN_PRED_CONSTR(GeExpr); 254 | BV_BIN_PRED_CONSTR(GtExpr); 255 | BV_BIN_PRED_CONSTR(UleExpr); 256 | BV_BIN_PRED_CONSTR(UltExpr); 257 | BV_BIN_PRED_CONSTR(UgeExpr); 258 | BV_BIN_PRED_CONSTR(UgtExpr); 259 | 260 | BV_BIN_OP_CONCRETE(AddExpr, a1 + a2); 261 | BV_BIN_OP_CONCRETE(SubExpr, a1 - a2); 262 | BV_BIN_OP_CONCRETE(MulExpr, a1 *a2); 263 | BV_BIN_OP_CONCRETE(DivExpr, (int64_t)a1 / (int64_t)a2); 264 | BV_BIN_OP_CONCRETE(ModExpr, (int64_t)a1 % (int64_t)a2); 265 | 266 | BV_BIN_OP_CONCRETE(UDivExpr, a1 / a2); 267 | BV_BIN_OP_CONCRETE(UModExpr, a1 % a2); 268 | 269 | BV_BIN_OP_CONCRETE(LAndExpr, (a1 != 0) & (a2 != 0)); 270 | BV_BIN_OP_CONCRETE(LOrExpr, (a1 != 0) | (a2 != 0)); 271 | BV_BIN_OP_CONCRETE(LXorExpr, (a1 != 0) ^ (a1 != 0)); 272 | BV_BIN_OP_CONCRETE(ImpliesExpr, !(a1 != 0) | (a2 != 0)); 273 | 274 | BV_BIN_OP_CONCRETE(AndExpr, a1 &a2); 275 | BV_BIN_OP_CONCRETE(OrExpr, a1 | a2); 276 | BV_BIN_OP_CONCRETE(XorExpr, a1 ^ a2); 277 | BV_BIN_OP_CONCRETE(LshExpr, a1 << a2); 278 | BV_BIN_OP_CONCRETE(LRshExpr, a1 >> a2); 279 | BV_BIN_OP_CONCRETE(ARshExpr, (int64_t)a1 >> a2); 280 | 281 | BV_BIN_OP_CONCRETE(EqExpr, a1 == a2); 282 | BV_BIN_OP_CONCRETE(NeqExpr, a1 != a2); 283 | BV_BIN_OP_CONCRETE(LeExpr, (int64_t)a1 <= (int64_t)a2); 284 | BV_BIN_OP_CONCRETE(LtExpr, (int64_t)a1 < (int64_t)a2); 285 | BV_BIN_OP_CONCRETE(GeExpr, (int64_t)a1 >= (int64_t)a2); 286 | BV_BIN_OP_CONCRETE(GtExpr, (int64_t)a1 > (int64_t)a2); 287 | BV_BIN_OP_CONCRETE(UleExpr, a1 <= a2); 288 | BV_BIN_OP_CONCRETE(UltExpr, a1 < a2); 289 | BV_BIN_OP_CONCRETE(UgeExpr, a1 >= a2); 290 | BV_BIN_OP_CONCRETE(UgtExpr, a1 > a2); 291 | 292 | ConcatExpr::ConcatExpr(const ArgList &args) : OpApplyNode(args) { 293 | int bw = 0; 294 | for (int i = 0; i < args.size(); i++) { 295 | if (!args[i]->type->is_bv_type()) { 296 | throw TypeCheckError{"concat expects bitvec"}; 297 | } 298 | bw += args[i]->type->get_bv_width(); 299 | } 300 | type = std::dynamic_pointer_cast(std::make_shared(bw)); 301 | } 302 | 303 | LNotExpr::LNotExpr(ExprPtr a) : OpApplyNode({a}) { type = a->type; } 304 | 305 | NotExpr::NotExpr(ExprPtr a) : OpApplyNode({a}) { type = a->type; } 306 | 307 | ExtractExpr::ExtractExpr(ExprPtr e, int start, int end) 308 | : v(e), from(start), to(end) { 309 | if (!v->type->is_bv_type()) { 310 | throw TypeCheckError{"Not BitVec"}; 311 | } 312 | 313 | if (to <= from || v->type->get_bv_width() < to) { 314 | throw TypeCheckError{"BV size error"}; 315 | } 316 | 317 | type = 318 | std::dynamic_pointer_cast(std::make_shared(to - from)); 319 | } 320 | 321 | SExtExpr::SExtExpr(ExprPtr e, int t) : v(e), to(t) { 322 | type = std::dynamic_pointer_cast(std::make_shared(to)); 323 | } 324 | 325 | UExtExpr::UExtExpr(ExprPtr e, int t) : v(e), to(t) { 326 | type = std::dynamic_pointer_cast(std::make_shared(to)); 327 | } 328 | 329 | IteExpr::IteExpr(ExprPtr c, ExprPtr t, ExprPtr f) 330 | : cond(c), t_val(t), f_val(f) { 331 | if (!t_val->type->equal_to(f_val->type)) { 332 | throw TypeCheckError{"Ite type mismatch"}; 333 | } 334 | type = t_val->type; 335 | } 336 | 337 | std::shared_ptr IteExpr::simplify() { 338 | auto c = cond->simplify(); 339 | auto t = t_val->simplify(); 340 | auto f = f_val->simplify(); 341 | 342 | if (c->is_symbolic()) { 343 | cond = c; 344 | t_val = t; 345 | f_val = f; 346 | return shared_from_this(); 347 | } else { 348 | // concrete condition 349 | auto concrete_cond = std::dynamic_pointer_cast(c); 350 | if (concrete_cond->get_val() == 1) { 351 | return t; 352 | } else { 353 | return f; 354 | } 355 | } 356 | } 357 | 358 | ForallExpr::ForallExpr(const std::vector &vs, ExprPtr c) { 359 | vars = vs; 360 | cond = c; 361 | 362 | for (auto &v : vs) { 363 | assert(v->type->is_bv_type()); 364 | } 365 | assert(c->type->is_bv_type()); 366 | assert(c->type->get_bv_width() == 1); 367 | 368 | auto t = std::make_shared(); 369 | t->bitwidth = 1; 370 | type = std::dynamic_pointer_cast(t); 371 | } 372 | 373 | std::shared_ptr ForallExpr::simplify() { 374 | cond = cond->simplify(); 375 | return shared_from_this(); 376 | } 377 | 378 | ExistsExpr::ExistsExpr(const std::vector &vs, ExprPtr c) { 379 | vars = vs; 380 | cond = c; 381 | 382 | for (auto &v : vs) { 383 | assert(v->type->is_bv_type()); 384 | } 385 | assert(c->type->is_bv_type()); 386 | assert(c->type->get_bv_width() == 1); 387 | 388 | auto t = std::make_shared(); 389 | t->bitwidth = 1; 390 | type = std::dynamic_pointer_cast(t); 391 | } 392 | 393 | std::shared_ptr ExistsExpr::simplify() { 394 | cond = cond->simplify(); 395 | return shared_from_this(); 396 | } 397 | 398 | #undef BV_BIN_PRED_CONSTR 399 | #undef BV_BIN_OP_CONSTR 400 | 401 | ExprPtr endian_reverse(ExprPtr val) { 402 | auto num_bits = val->type->get_bv_width(); 403 | assert(num_bits % 8 == 0); 404 | auto num_bytes = num_bits / 8; 405 | 406 | std::vector bytes; 407 | for (int i = 0; i < num_bytes; i++) { 408 | bytes.push_back(mk_expr_ptr(ExtractExpr, val, i * 8, (i + 1) * 8)); 409 | } 410 | 411 | ExprPtr result = nullptr; 412 | for (int i = 0; i < num_bytes; i++) { 413 | if (result == nullptr) { 414 | result = bytes[i]; 415 | } else { 416 | result = mk_expr_ptr(ConcatExpr, {result, bytes[i]}); 417 | } 418 | } 419 | return result->simplify(); 420 | } 421 | 422 | uint64_t get_concrete_val(ExprPtr v) { 423 | assert(!v->is_symbolic()); 424 | return std::dynamic_pointer_cast(v)->get_val(); 425 | } 426 | 427 | #define DEF_PRINT(T) virtual void visit_expr(T &e) override 428 | 429 | // just print type 430 | #define DEF_NAIVE_PRINT(T) \ 431 | virtual void visit_expr(T &e) override { os << "(some " #T " )"; } 432 | 433 | // OpApplyNode print 434 | #define DEF_OPAPPLY_PRINT(T) \ 435 | virtual void visit_expr(T &e) override { \ 436 | os << "(" #T " "; \ 437 | for (auto &a : e.args_) { \ 438 | print_expr(a, os); \ 439 | os << " "; \ 440 | } \ 441 | } 442 | 443 | class ExprPrintVisitor : public ExprVisitor { 444 | public: 445 | ExprPrintVisitor(std::ostream &_os) : os(_os) {} 446 | 447 | DEF_NAIVE_PRINT(Expr); 448 | DEF_NAIVE_PRINT(Lambda); 449 | DEF_PRINT(ConcreteBv) { os << e.get_val(); } 450 | DEF_NAIVE_PRINT(SymbolicVar); 451 | 452 | DEF_OPAPPLY_PRINT(AddExpr); 453 | DEF_OPAPPLY_PRINT(SubExpr); 454 | DEF_OPAPPLY_PRINT(MulExpr); 455 | DEF_OPAPPLY_PRINT(DivExpr); 456 | DEF_OPAPPLY_PRINT(ModExpr); 457 | DEF_OPAPPLY_PRINT(UDivExpr); 458 | DEF_OPAPPLY_PRINT(UModExpr); 459 | 460 | DEF_OPAPPLY_PRINT(LAndExpr); 461 | DEF_OPAPPLY_PRINT(LOrExpr); 462 | DEF_OPAPPLY_PRINT(LXorExpr); 463 | DEF_OPAPPLY_PRINT(LNotExpr); 464 | DEF_OPAPPLY_PRINT(ImpliesExpr); 465 | 466 | DEF_OPAPPLY_PRINT(AndExpr); 467 | DEF_OPAPPLY_PRINT(OrExpr); 468 | DEF_OPAPPLY_PRINT(XorExpr); 469 | DEF_OPAPPLY_PRINT(NotExpr); 470 | DEF_OPAPPLY_PRINT(LshExpr); 471 | DEF_OPAPPLY_PRINT(LRshExpr); 472 | DEF_OPAPPLY_PRINT(ARshExpr); 473 | 474 | DEF_OPAPPLY_PRINT(EqExpr); 475 | DEF_OPAPPLY_PRINT(NeqExpr); 476 | DEF_OPAPPLY_PRINT(LeExpr); 477 | DEF_OPAPPLY_PRINT(LtExpr); 478 | DEF_OPAPPLY_PRINT(GeExpr); 479 | DEF_OPAPPLY_PRINT(GtExpr); 480 | DEF_OPAPPLY_PRINT(UleExpr); 481 | DEF_OPAPPLY_PRINT(UltExpr); 482 | DEF_OPAPPLY_PRINT(UgeExpr); 483 | DEF_OPAPPLY_PRINT(UgtExpr); 484 | 485 | DEF_NAIVE_PRINT(ExtractExpr); 486 | DEF_NAIVE_PRINT(SExtExpr); 487 | DEF_NAIVE_PRINT(UExtExpr); 488 | 489 | DEF_NAIVE_PRINT(IteExpr); 490 | DEF_NAIVE_PRINT(FuncApply); 491 | DEF_OPAPPLY_PRINT(ConcatExpr); 492 | 493 | DEF_NAIVE_PRINT(ForallExpr); 494 | DEF_NAIVE_PRINT(ExistsExpr); 495 | 496 | std::ostream &os; 497 | }; 498 | 499 | #undef DEF_PRINT 500 | #undef DEF_NAIVE_PRINT 501 | #undef DEF_OPAPPLY_PRINT 502 | 503 | void print_expr(ExprPtr expr, std::ostream &os) { 504 | ExprPrintVisitor v(os); 505 | v.visit(*expr); 506 | } 507 | 508 | } // namespace Symbolic 509 | 510 | std::shared_ptr mk_bv_type(int bitwidth) { 511 | return std::make_shared(bitwidth); 512 | } 513 | 514 | std::shared_ptr mk_bv_var(int bitwidth, 515 | const std::string &name) { 516 | auto t = mk_bv_type(bitwidth); 517 | return mk_expr_ptr(SymbolicVar, t, name); 518 | } 519 | -------------------------------------------------------------------------------- /verifier/src/utils.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "utils.h" 5 | 6 | extern "C" { 7 | #include 8 | #include 9 | #include 10 | } 11 | 12 | bool is_prefix(const std::string &s, const std::string &prefix) { 13 | auto res = std::mismatch(prefix.begin(), prefix.end(), s.begin()); 14 | if (res.first == prefix.end()) { 15 | return true; 16 | } 17 | return false; 18 | } 19 | 20 | std::string NameFactory::GetUniqueName(const std::string &base_name) { 21 | std::lock_guard lg(name_map_mutex_); 22 | if (name_map_.find(base_name) == name_map_.end()) { 23 | name_map_[base_name] = 0; 24 | } 25 | uint64_t cnt = name_map_[base_name]; 26 | assert(cnt < 0xffffffffffffffffULL); 27 | name_map_[base_name] = cnt + 1; 28 | return base_name + "!" + std::to_string(cnt); 29 | } 30 | 31 | SubProcessFunc::SubProcessFunc(std::function f) : f_(f) {} 32 | 33 | std::string SubProcessFunc::operator()() { 34 | pid_t pid; 35 | int pipe_fd[2]; 36 | if (pipe(pipe_fd)) { 37 | throw Exception{"Pipe Failed"}; 38 | } 39 | 40 | pid = fork(); 41 | if (pid < (pid_t)0) { 42 | throw Exception{"Fork Failed"}; 43 | } 44 | 45 | if (pid == (pid_t)0) { 46 | // child process 47 | close(pipe_fd[0]); 48 | auto str = f_(); 49 | ssize_t offset = 0; 50 | ssize_t written = 0; 51 | const char *ptr = str.c_str(); 52 | do { 53 | written = write(pipe_fd[1], &ptr[offset], str.size() - offset); 54 | if (written < 0) { 55 | exit(1); 56 | } 57 | offset += written; 58 | } while (offset < str.size()); 59 | close(pipe_fd[1]); 60 | exit(0); 61 | } else { 62 | // parent process 63 | close(pipe_fd[1]); 64 | int ret_code = 0; 65 | ssize_t bytes_read = 0; 66 | char buf[65538]; 67 | std::string result = ""; 68 | do { 69 | bytes_read = read(pipe_fd[0], buf, 65536); 70 | if (bytes_read < 0) { 71 | throw Exception{"Pipe Reading Failed"}; 72 | } 73 | result = result + std::string(buf, bytes_read); 74 | } while (bytes_read != 0); 75 | waitpid(pid, &ret_code, 0); 76 | if (ret_code != 0) { 77 | throw Exception{"Child Process Failed"}; 78 | } 79 | close(pipe_fd[0]); 80 | return result; 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /verifier/src/z3-gen.cc: -------------------------------------------------------------------------------- 1 | #include "z3-gen.h" 2 | #include "utils.h" 3 | 4 | namespace Symbolic { 5 | 6 | z3::expr bool_to_bv1(z3::context &ctx, z3::expr &v) { 7 | return z3::ite(v, ctx.bv_val(1, 1), ctx.bv_val(0, 1)); 8 | } 9 | 10 | z3::expr bv1_to_bool(z3::expr &v) { return v == 1; } 11 | 12 | bool Z3Expr::is_bool() const { 13 | return this->is_expr() && std::get(content).is_bool(); 14 | } 15 | 16 | bool Z3Expr::is_expr() const { 17 | return std::holds_alternative(content); 18 | } 19 | 20 | bool Z3Expr::is_func() const { 21 | return std::holds_alternative(content); 22 | } 23 | 24 | z3::expr Z3Expr::get_expr() { return std::get(content); } 25 | 26 | z3::expr Z3Expr::get_bool() { 27 | auto e = std::get(content); 28 | if (e.is_bv()) { 29 | return bv1_to_bool(std::get(content)); 30 | } else { 31 | return e; 32 | } 33 | } 34 | 35 | z3::func_decl Z3Expr::get_func() { return std::get(content); } 36 | 37 | #define DECL_VISITOR(T) virtual void visit_expr(T &e) override; 38 | 39 | class Z3GenVisitor : public ExprVisitor { 40 | public: 41 | Z3Context &my_ctx; 42 | z3::context &ctx; 43 | std::variant result; 44 | 45 | Z3GenVisitor(Z3Context &c) : my_ctx(c), ctx(my_ctx.ctx), result(0) {} 46 | 47 | DECL_VISITOR(Expr); 48 | DECL_VISITOR(Lambda); 49 | DECL_VISITOR(ConcreteBv); 50 | DECL_VISITOR(SymbolicVar); 51 | 52 | DECL_VISITOR(AddExpr); 53 | DECL_VISITOR(SubExpr); 54 | DECL_VISITOR(MulExpr); 55 | DECL_VISITOR(DivExpr); 56 | DECL_VISITOR(ModExpr); 57 | DECL_VISITOR(UDivExpr); 58 | DECL_VISITOR(UModExpr); 59 | 60 | DECL_VISITOR(LAndExpr); 61 | DECL_VISITOR(LOrExpr); 62 | DECL_VISITOR(LXorExpr); 63 | DECL_VISITOR(LNotExpr); 64 | DECL_VISITOR(ImpliesExpr); 65 | 66 | DECL_VISITOR(AndExpr); 67 | DECL_VISITOR(OrExpr); 68 | DECL_VISITOR(XorExpr); 69 | DECL_VISITOR(NotExpr); 70 | DECL_VISITOR(LshExpr); 71 | DECL_VISITOR(LRshExpr); 72 | DECL_VISITOR(ARshExpr); 73 | 74 | DECL_VISITOR(EqExpr); 75 | DECL_VISITOR(NeqExpr); 76 | DECL_VISITOR(LeExpr); 77 | DECL_VISITOR(LtExpr); 78 | DECL_VISITOR(GeExpr); 79 | DECL_VISITOR(GtExpr); 80 | DECL_VISITOR(UleExpr); 81 | DECL_VISITOR(UltExpr); 82 | DECL_VISITOR(UgeExpr); 83 | DECL_VISITOR(UgtExpr); 84 | 85 | DECL_VISITOR(ExtractExpr); 86 | DECL_VISITOR(SExtExpr); 87 | DECL_VISITOR(UExtExpr); 88 | 89 | DECL_VISITOR(IteExpr); 90 | DECL_VISITOR(FuncApply); 91 | DECL_VISITOR(ConcatExpr); 92 | 93 | DECL_VISITOR(ForallExpr); 94 | DECL_VISITOR(ExistsExpr); 95 | }; 96 | 97 | #undef DECL_VISITOR 98 | #define VISITOR_IMPL(T) void Z3GenVisitor::visit_expr(T &e) 99 | #define LOAD_BIN_OPRAND \ 100 | auto &args = e.args_; \ 101 | assert(args.size() == 2); \ 102 | auto a1 = gen_z3_expr(my_ctx, args[0]); \ 103 | auto a2 = gen_z3_expr(my_ctx, args[1]) 104 | 105 | VISITOR_IMPL(Expr) {} 106 | VISITOR_IMPL(Lambda) { throw "could not gen z3 expr from lambda"; } 107 | VISITOR_IMPL(ConcreteBv) { 108 | auto t = e.type; 109 | result = ctx.bv_val(e.get_val(), t->get_bv_width()); 110 | } 111 | VISITOR_IMPL(SymbolicVar) { 112 | auto t = e.type; 113 | if (t->is_bv_type()) { 114 | result = ctx.bv_const(e.name.c_str(), t->get_bv_width()); 115 | } else if (t->is_func_type()) { 116 | auto t = std::dynamic_pointer_cast(e.type); 117 | z3::sort_vector key_sorts(ctx); 118 | for (int i = 0; i < t->key_types.size(); i++) { 119 | auto s = ctx.bv_sort(t->key_types[i]->get_bv_width()); 120 | key_sorts.push_back(s); 121 | } 122 | auto val_sort = ctx.bv_sort(t->val_type->get_bv_width()); 123 | result = ctx.function(e.name.c_str(), key_sorts, val_sort); 124 | } 125 | } 126 | 127 | VISITOR_IMPL(AddExpr) { 128 | LOAD_BIN_OPRAND; 129 | result = a1.get_expr() + a2.get_expr(); 130 | } 131 | VISITOR_IMPL(SubExpr) { 132 | LOAD_BIN_OPRAND; 133 | result = a1.get_expr() - a2.get_expr(); 134 | } 135 | VISITOR_IMPL(MulExpr) { 136 | LOAD_BIN_OPRAND; 137 | result = a1.get_expr() * a2.get_expr(); 138 | } 139 | VISITOR_IMPL(DivExpr) { 140 | LOAD_BIN_OPRAND; 141 | result = a1.get_expr() / a2.get_expr(); 142 | } 143 | VISITOR_IMPL(ModExpr) { 144 | LOAD_BIN_OPRAND; 145 | result = a1.get_expr() % a2.get_expr(); 146 | } 147 | VISITOR_IMPL(UDivExpr) { 148 | LOAD_BIN_OPRAND; 149 | result = z3::udiv(a1.get_expr(), a2.get_expr()); 150 | } 151 | VISITOR_IMPL(UModExpr) { 152 | LOAD_BIN_OPRAND; 153 | result = z3::urem(a1.get_expr(), a2.get_expr()); 154 | } 155 | 156 | VISITOR_IMPL(LAndExpr) { 157 | LOAD_BIN_OPRAND; 158 | result = a1.get_bool() && a2.get_bool(); 159 | } 160 | VISITOR_IMPL(LOrExpr) { 161 | LOAD_BIN_OPRAND; 162 | result = a1.get_bool() || a2.get_bool(); 163 | } 164 | VISITOR_IMPL(LXorExpr) { 165 | LOAD_BIN_OPRAND; 166 | result = a1.get_bool() ^ a2.get_bool(); 167 | } 168 | VISITOR_IMPL(ImpliesExpr) { 169 | LOAD_BIN_OPRAND; 170 | result = z3::implies(a1.get_bool(), a2.get_bool()); 171 | } 172 | 173 | VISITOR_IMPL(AndExpr) { 174 | LOAD_BIN_OPRAND; 175 | if (e.args_[0]->type->get_bv_width() == 1) { 176 | result = a1.get_bool() && a2.get_bool(); 177 | } else { 178 | result = a1.get_expr() & a2.get_expr(); 179 | } 180 | } 181 | VISITOR_IMPL(OrExpr) { 182 | LOAD_BIN_OPRAND; 183 | if (e.args_[0]->type->get_bv_width() == 1) { 184 | result = a1.get_bool() || a2.get_bool(); 185 | } else { 186 | result = a1.get_expr() | a2.get_expr(); 187 | } 188 | } 189 | VISITOR_IMPL(XorExpr) { 190 | LOAD_BIN_OPRAND; 191 | if (e.args_[0]->type->get_bv_width() == 1) { 192 | // need to use the C version instead 193 | auto a = a1.get_bool(); 194 | auto b = a2.get_bool(); 195 | check_context(a, b); 196 | assert(a.is_bool() && b.is_bool()); 197 | result = z3::expr(a.ctx(), Z3_mk_xor(a.ctx(), a, b)); 198 | } else { 199 | result = a1.get_expr() ^ a2.get_expr(); 200 | } 201 | } 202 | VISITOR_IMPL(LshExpr) { 203 | LOAD_BIN_OPRAND; 204 | result = z3::shl(a1.get_expr(), a2.get_expr()); 205 | } 206 | VISITOR_IMPL(LRshExpr) { 207 | LOAD_BIN_OPRAND; 208 | result = z3::lshr(a1.get_expr(), a2.get_expr()); 209 | } 210 | VISITOR_IMPL(ARshExpr) { 211 | LOAD_BIN_OPRAND; 212 | result = z3::ashr(a1.get_expr(), a2.get_expr()); 213 | } 214 | 215 | VISITOR_IMPL(EqExpr) { 216 | LOAD_BIN_OPRAND; 217 | result = (a1.get_expr() == a2.get_expr()); 218 | } 219 | VISITOR_IMPL(NeqExpr) { 220 | LOAD_BIN_OPRAND; 221 | result = (a1.get_expr() != a2.get_expr()); 222 | } 223 | VISITOR_IMPL(LeExpr) { 224 | LOAD_BIN_OPRAND; 225 | result = (a1.get_expr() <= a2.get_expr()); 226 | } 227 | VISITOR_IMPL(LtExpr) { 228 | LOAD_BIN_OPRAND; 229 | result = (a1.get_expr() < a2.get_expr()); 230 | } 231 | VISITOR_IMPL(GeExpr) { 232 | LOAD_BIN_OPRAND; 233 | result = (a1.get_expr() >= a2.get_expr()); 234 | } 235 | VISITOR_IMPL(GtExpr) { 236 | LOAD_BIN_OPRAND; 237 | result = (a1.get_expr() > a2.get_expr()); 238 | } 239 | VISITOR_IMPL(UleExpr) { 240 | LOAD_BIN_OPRAND; 241 | result = z3::ule(a1.get_expr(), a2.get_expr()); 242 | } 243 | VISITOR_IMPL(UltExpr) { 244 | LOAD_BIN_OPRAND; 245 | result = z3::ult(a1.get_expr(), a2.get_expr()); 246 | } 247 | VISITOR_IMPL(UgeExpr) { 248 | LOAD_BIN_OPRAND; 249 | result = z3::uge(a1.get_expr(), a2.get_expr()); 250 | } 251 | VISITOR_IMPL(UgtExpr) { 252 | LOAD_BIN_OPRAND; 253 | result = z3::ugt(a1.get_expr(), a2.get_expr()); 254 | } 255 | VISITOR_IMPL(LNotExpr) { 256 | auto a = gen_z3_expr(my_ctx, e.args_[0]); 257 | result = !a.get_bool(); 258 | } 259 | VISITOR_IMPL(NotExpr) { 260 | auto a = gen_z3_expr(my_ctx, e.args_[0]); 261 | if (e.args_[0]->type->get_bv_width() == 1) { 262 | result = !a.get_bool(); 263 | } else { 264 | result = ~a.get_expr(); 265 | } 266 | } 267 | VISITOR_IMPL(ExtractExpr) { 268 | auto a = gen_z3_expr(my_ctx, e.v); 269 | result = a.get_expr().extract(e.to - 1, e.from); 270 | } 271 | VISITOR_IMPL(SExtExpr) { 272 | auto a = gen_z3_expr(my_ctx, e.v); 273 | auto bw = e.v->type->get_bv_width(); 274 | result = z3::sext(a.get_expr(), e.to - bw); 275 | } 276 | VISITOR_IMPL(UExtExpr) { 277 | auto a = gen_z3_expr(my_ctx, e.v); 278 | auto bw = e.v->type->get_bv_width(); 279 | result = z3::zext(a.get_expr(), e.to - bw); 280 | } 281 | VISITOR_IMPL(IteExpr) { 282 | auto c = gen_z3_expr(my_ctx, e.cond).get_bool(); 283 | auto t = gen_z3_expr(my_ctx, e.t_val).get_expr(); 284 | auto f = gen_z3_expr(my_ctx, e.f_val).get_expr(); 285 | result = z3::ite(c, t, f); 286 | } 287 | VISITOR_IMPL(FuncApply) { 288 | auto f = e.func; 289 | if (f->is_symbolic()) { 290 | auto func = gen_z3_expr(my_ctx, f).get_func(); 291 | z3::expr_vector args(my_ctx.ctx); 292 | for (int i = 0; i < e.args_.size(); i++) { 293 | args.push_back(gen_z3_expr(my_ctx, e.args_[i]).get_expr()); 294 | } 295 | result = func(args); 296 | } else { 297 | // func is lambda; 298 | auto func = std::dynamic_pointer_cast(f)->func; 299 | auto val = func(e.args_); 300 | result = gen_z3_expr(my_ctx, val).get_expr(); 301 | } 302 | } 303 | VISITOR_IMPL(ConcatExpr) { 304 | z3::expr_vector bv_vec(my_ctx.ctx); 305 | for (int i = 0; i < e.args_.size(); i++) { 306 | auto expr = gen_z3_expr(my_ctx, e.args_[i]).get_expr(); 307 | bv_vec.push_back(expr); 308 | } 309 | result = z3::concat(bv_vec); 310 | } 311 | 312 | VISITOR_IMPL(ForallExpr) { 313 | z3::expr_vector var_vec(my_ctx.ctx); 314 | for (int i = 0; i < e.vars.size(); i++) { 315 | auto expr = gen_z3_expr(my_ctx, e.vars[i]).get_expr(); 316 | var_vec.push_back(expr); 317 | } 318 | auto cond = gen_z3_expr(my_ctx, e.cond).get_bool(); 319 | result = z3::forall(var_vec, cond); 320 | } 321 | 322 | VISITOR_IMPL(ExistsExpr) { 323 | z3::expr_vector var_vec(my_ctx.ctx); 324 | for (int i = 0; i < e.vars.size(); i++) { 325 | auto expr = gen_z3_expr(my_ctx, e.vars[i]).get_expr(); 326 | var_vec.push_back(expr); 327 | } 328 | auto cond = gen_z3_expr(my_ctx, e.cond).get_bool(); 329 | result = z3::exists(var_vec, cond); 330 | } 331 | 332 | #undef VISITOR_IMPL 333 | 334 | Z3Expr gen_z3_expr(Z3Context &ctx, ExprPtr expr) { 335 | auto uuid = expr->get_uuid(); 336 | if (ctx.cache_.find(uuid) != ctx.cache_.end()) { 337 | return ctx.cache_[uuid]; 338 | } 339 | if (expr->is_var()) { 340 | auto e = std::dynamic_pointer_cast(expr); 341 | if (ctx.var_cache_.find(e->name) != ctx.var_cache_.end()) { 342 | return ctx.var_cache_[e->name]; 343 | } 344 | } 345 | Z3GenVisitor visitor(ctx); 346 | visitor.visit(*expr); 347 | Z3Expr result; 348 | result.content = visitor.result; 349 | ctx.cache_[uuid] = result; 350 | if (expr->is_var()) { 351 | auto e = std::dynamic_pointer_cast(expr); 352 | ctx.var_cache_[e->name] = result; 353 | } 354 | return result; 355 | } 356 | 357 | bool verify_with_z3(Z3Context &ctx, ExprPtr pre_cond, ExprPtr target, 358 | bool do_fork) { 359 | std::function f = [&]() -> std::string { 360 | if (pre_cond == nullptr) { 361 | pre_cond = mk_concrete_bv(1, 1); 362 | } 363 | if (target == nullptr) { 364 | target = mk_concrete_bv(1, 0); 365 | } 366 | 367 | assert(pre_cond->type->get_bv_width() == 1); 368 | assert(target->type->get_bv_width() == 1); 369 | 370 | auto pre = gen_z3_expr(ctx, pre_cond).get_bool(); 371 | auto post = gen_z3_expr(ctx, target).get_bool(); 372 | z3::solver sol(ctx.ctx); 373 | sol.add(pre); 374 | sol.add(!post); 375 | if (sol.check() == z3::unsat) { 376 | return "verified"; 377 | } else { 378 | return "unverified"; 379 | } 380 | }; 381 | if (do_fork) { 382 | SubProcessFunc remote_f(f); 383 | return remote_f() == "verified"; 384 | } else { 385 | return f() == "verified"; 386 | } 387 | } 388 | 389 | void print_expr_z3(ExprPtr expr, std::ostream &os) { 390 | Z3Context ctx; 391 | auto e = gen_z3_expr(ctx, expr); 392 | os << e.get_expr().simplify(); 393 | } 394 | } // namespace Symbolic 395 | -------------------------------------------------------------------------------- /verifier/undo_log.dfy: -------------------------------------------------------------------------------- 1 | class CrashableMem { 2 | var mem_ : array; 3 | method read(off : int) returns (r : T) 4 | requires 0 <= off < mem_.Length; 5 | { 6 | return mem_[off]; 7 | } 8 | 9 | method write(off : int, val : T) 10 | requires 0 <= off < mem_.Length; 11 | modifies mem_; 12 | { 13 | mem_[off] := val; 14 | } 15 | } 16 | 17 | datatype GhostState = GS( 18 | num_entry : int, 19 | log : seq, 20 | 21 | mem_len : int, 22 | mem : seq, 23 | old_mem : seq, 24 | ideal_mem : seq, 25 | countdown : int, 26 | first_log_pos : map 27 | ) 28 | 29 | datatype GhostOp = WriteMem(off : int, val : int) 30 | | WriteLog(off : int, val : int) 31 | predicate ghost_state_inv(s : GhostState) { 32 | 0 <= s.num_entry * 2 < |s.log| 33 | && |s.log| > 0 34 | && |s.mem| == s.mem_len && |s.ideal_mem| == s.mem_len && |s.old_mem| == s.mem_len 35 | && s.countdown >= 0 36 | } 37 | 38 | function init_ghost_state(log : seq, mem : seq, countdown : int) : GhostState 39 | requires |log| > 0; 40 | requires countdown >= 0; 41 | ensures ghost_state_inv(init_ghost_state(log, mem, countdown)); 42 | { 43 | GS(0, log[..], |mem|, mem[..], mem[..], mem[..], countdown, map[]) 44 | } 45 | 46 | function mem_write(s : GhostState, off: int, val: int) : GhostState 47 | requires ghost_state_inv(s); 48 | requires 0 <= off < s.mem_len; 49 | ensures ghost_state_inv(mem_write(s, off, val)); 50 | { 51 | var new_mem := s.mem[off := val]; 52 | var new_ideal_mem := s.ideal_mem[off := val]; 53 | s.(mem := new_mem, 54 | ideal_mem := new_ideal_mem) 55 | } 56 | 57 | function log_write(s : GhostState, off : int, val: int) : GhostState 58 | requires ghost_state_inv(s); 59 | requires 0 <= off < |s.log|; 60 | ensures ghost_state_inv(log_write(s, off, val)); 61 | { 62 | s.(log := s.log[off := val]) 63 | } 64 | 65 | predicate valid_op(s : GhostState, op : GhostOp) 66 | { 67 | match op 68 | case WriteMem(off, val) => 0 <= off < |s.mem| 69 | case WriteLog(off, val) => 0 <= off < |s.log| 70 | } 71 | 72 | function countdown (s : GhostState) : GhostState 73 | { 74 | if s.countdown > 0 then 75 | s.(countdown := s.countdown - 1) 76 | else 77 | s 78 | } 79 | 80 | function normal_step (s : GhostState, op : GhostOp) : GhostState 81 | requires valid_op(s, op); 82 | requires ghost_state_inv(s); 83 | ensures ghost_state_inv(normal_step(s, op)); 84 | { 85 | match op 86 | case WriteMem(off, val) => mem_write(s, off, val) 87 | case WriteLog(off, val) => log_write(s, off, val) 88 | } 89 | 90 | function ghost_step (s : GhostState, op : GhostOp) : (GhostState, bool) 91 | requires valid_op(s, op); 92 | requires ghost_state_inv(s); 93 | ensures ghost_state_inv(normal_step(s, op)); 94 | { 95 | if s.countdown > 0 then 96 | var s' := normal_step(s, op); 97 | (s'.(countdown := s.countdown - 1), true) 98 | else 99 | (s, false) 100 | } 101 | 102 | function mem_write_step (s : GhostState, off : int, val : int) : (GhostState, bool) 103 | requires 0 <= off < s.mem_len; 104 | requires ghost_state_inv(s); 105 | { 106 | ghost_step(s, WriteMem(off, val)) 107 | } 108 | 109 | function log_write_step (s : GhostState, off : int, val : int) : (GhostState, bool) 110 | requires 0 <= off < |s.log|; 111 | requires ghost_state_inv(s); 112 | { 113 | ghost_step(s, WriteLog(off, val)) 114 | } 115 | 116 | function set_num_entry (s : GhostState, n : int) : (GhostState, bool) 117 | requires 0 <= n * 2 < |s.log|; 118 | { 119 | if s.countdown > 0 then 120 | (s.(num_entry := n, 121 | countdown := s.countdown - 1), 122 | true) 123 | else 124 | (s, false) 125 | } 126 | 127 | predicate crashed (s : GhostState) 128 | { 129 | s.countdown <= 0 130 | } 131 | 132 | predicate old_mem_equiv (s : GhostState) 133 | requires ghost_state_inv(s); 134 | { 135 | (forall o :: !(o in s.first_log_pos) && 0 <= o < |s.mem| ==> s.mem[o] == s.old_mem[o]) 136 | } 137 | 138 | predicate ghost_tx_inv (s : GhostState) 139 | { 140 | ghost_state_inv(s) && 141 | (forall o :: o in s.first_log_pos ==> 0 <= o < s.mem_len) && 142 | (forall o :: o in s.first_log_pos ==> 0 <= s.first_log_pos[o] < s.num_entry) && 143 | (forall o :: o in s.first_log_pos ==> 0 <= s.first_log_pos[o] * 2 + 1 < |s.log|) && 144 | (forall o :: o in s.first_log_pos ==> s.log[s.first_log_pos[o] * 2] == o) && 145 | (forall o :: o in s.first_log_pos ==> s.log[s.first_log_pos[o] * 2 + 1] == s.old_mem[o]) && 146 | (forall o :: o in s.first_log_pos ==> forall i :: 0 <= i < s.first_log_pos[o] ==> s.log[i * 2] != o) && 147 | (forall i :: 0 <= i < s.num_entry ==> s.log[i * 2] in s.first_log_pos) 148 | } 149 | 150 | function ghost_begin_tx (s : GhostState) : GhostState 151 | requires ghost_state_inv(s); 152 | requires s.num_entry == 0; 153 | ensures ghost_state_inv(ghost_begin_tx(s)); 154 | ensures ghost_tx_inv(ghost_begin_tx(s)); 155 | ensures old_mem_equiv(ghost_begin_tx(s)); 156 | { 157 | var (s', f) := set_num_entry(s, 0); 158 | var s' := s'.(first_log_pos := map[], old_mem := s.mem[..]); 159 | s' 160 | } 161 | 162 | function ghost_commit_tx (s : GhostState) : (GhostState, bool) 163 | requires ghost_tx_inv(s); 164 | requires old_mem_equiv(s); 165 | ensures ghost_state_inv(ghost_commit_tx(s).0); 166 | ensures ghost_tx_inv(ghost_commit_tx(s).0); 167 | ensures !ghost_commit_tx(s).1 ==> old_mem_equiv(ghost_commit_tx(s).0); 168 | ensures ghost_commit_tx(s).1 ==> ghost_commit_tx(s).0.num_entry == 0; 169 | { 170 | var s' := s; 171 | var (s', f) := set_num_entry(s', 0); 172 | var s' := if f then s'.(first_log_pos := map[]) else s'; 173 | (s', f) 174 | } 175 | 176 | function ghost_tx_write (s0 : GhostState, off : int, val : int) : GhostState 177 | requires ghost_tx_inv(s0); 178 | requires old_mem_equiv(s0); 179 | requires 0 <= off < s0.mem_len; 180 | requires 0 <= s0.num_entry * 2 + 2 < |s0.log|; 181 | ensures ghost_tx_inv(ghost_tx_write(s0, off, val)); 182 | ensures old_mem_equiv(ghost_tx_write(s0, off, val)); 183 | ensures |ghost_tx_write(s0, off, val).mem| == s0.mem_len; 184 | ensures !crashed(ghost_tx_write(s0, off, val)) ==> ghost_tx_write(s0, off, val).mem[off] == val; 185 | { 186 | var s := s0; 187 | var log_idx := s.num_entry; 188 | var log_off := log_idx * 2; 189 | var old_val := s.mem[off]; 190 | var (s, f) := log_write_step(s, log_off, off); 191 | var (s, f) := log_write_step(s, log_off + 1, old_val); 192 | var (s, f) := set_num_entry(s, log_idx + 1); 193 | var s := if f && !(off in s.first_log_pos) 194 | then s.(first_log_pos := s.first_log_pos[off := log_idx]) 195 | else s; 196 | var (s, f) := mem_write_step(s, off, val); 197 | s 198 | } 199 | 200 | function reverse_recovery (s0 : GhostState, idx : int) : GhostState 201 | decreases idx; 202 | requires ghost_tx_inv(s0); 203 | requires old_mem_equiv(s0); 204 | requires 0 <= idx <= s0.num_entry; 205 | ensures ghost_tx_inv(reverse_recovery(s0, idx)); 206 | ensures old_mem_equiv(reverse_recovery(s0, idx)); 207 | ensures s0.old_mem == reverse_recovery(s0, idx).old_mem; 208 | ensures s0.first_log_pos == reverse_recovery(s0, idx).first_log_pos; 209 | ensures forall o :: o in s0.first_log_pos && s0.first_log_pos[o] >= idx ==> 210 | reverse_recovery(s0, idx).mem[o] == s0.mem[o]; 211 | ensures forall o :: o in s0.first_log_pos && 0 <= s0.first_log_pos[o] < idx ==> 212 | reverse_recovery(s0, idx).mem[o] == s0.old_mem[o]; 213 | { 214 | if idx == 0 then 215 | assert old_mem_equiv(s0); 216 | s0 217 | else 218 | var s := s0; 219 | var i := idx - 1; 220 | var off := s.log[i * 2]; 221 | var val := s.log[i * 2 + 1]; 222 | var s := s.(mem := s.mem[off := val]); 223 | assert off in s.first_log_pos; 224 | var s := reverse_recovery(s, idx - 1); 225 | assert i == idx - 1; 226 | assert forall o :: o in s.first_log_pos && 0 <= s.first_log_pos[o] < i ==> 227 | s.mem[o] == s.old_mem[o]; 228 | assert forall o :: o in s.first_log_pos && s.first_log_pos[o] == i ==> 229 | o == off && val == s.old_mem[o]; 230 | assert forall o :: o in s.first_log_pos && s.first_log_pos[o] == i ==> 231 | s.mem[o] == val; 232 | assert old_mem_equiv(s); 233 | s 234 | } 235 | 236 | function ghost_recover (s0 : GhostState) : GhostState 237 | requires ghost_tx_inv(s0); 238 | requires old_mem_equiv(s0); 239 | ensures ghost_recover(s0).mem == s0.old_mem; 240 | ensures ghost_recover(s0).num_entry == 0; 241 | { 242 | var s := reverse_recovery(s0, s0.num_entry); 243 | assert (old_mem_equiv(s)); 244 | assert (forall o :: o in s.first_log_pos ==> s.mem[o] == s0.old_mem[o]); 245 | assert forall i :: 0 <= i < |s.mem| ==> s.mem[i] == s0.old_mem[i]; 246 | s.(num_entry := 0) 247 | } 248 | 249 | 250 | class UndoLog { 251 | var log_ : array; 252 | var mem_ : array; 253 | 254 | var impl_countdown : int; 255 | ghost var gs : GhostState; 256 | 257 | constructor () {} 258 | 259 | predicate ghost_state_equiv(gs : GhostState) 260 | reads this; 261 | reads mem_; 262 | reads log_; 263 | { 264 | log_.Length > 0 && 265 | mem_[..] == gs.mem && 266 | log_[1..] == gs.log && 267 | log_[0] == gs.num_entry && 268 | impl_countdown == gs.countdown 269 | } 270 | predicate state_inv() 271 | reads this; 272 | reads log_; 273 | { 274 | log_.Length > 1 && 0 <= log_[0] && (log_[0] * 2) < log_.Length 275 | && log_.Length < 0xffffffff && mem_ != log_ 276 | && forall i : int :: 0 <= i < log_[0] ==> 0 <= log_[i * 2 + 1] < mem_.Length 277 | && impl_countdown >= 0 278 | } 279 | 280 | method init(log_size : int, mem_size : int, countdown : int) 281 | requires log_size > 1; 282 | requires mem_size > 0; 283 | requires log_size < 0xffffffff; 284 | modifies this; 285 | ensures fresh(log_); 286 | ensures fresh(mem_); 287 | ensures state_inv(); 288 | ensures ghost_state_equiv(gs); 289 | { 290 | log_ := new int[log_size]; 291 | mem_ := new int[mem_size]; 292 | log_[0] := 0; 293 | 294 | impl_countdown := countdown; 295 | gs := GS(0, log_[1..], mem_size, mem_[..], mem_[..], mem_[..], countdown, map[]); 296 | } 297 | 298 | method impl_countdown_dec() 299 | modifies this; 300 | requires impl_countdown > 0; 301 | requires mem_ != log_; 302 | ensures mem_ != log_; 303 | ensures impl_countdown == old(impl_countdown) - 1; 304 | ensures impl_countdown >= 0; 305 | ensures gs == old(gs); 306 | ensures log_[..] == old(log_)[..]; 307 | ensures mem_[..] == old(mem_)[..]; 308 | { 309 | impl_countdown := impl_countdown - 1; 310 | } 311 | 312 | method write_mem(off : int, val : int) 313 | modifies this; 314 | modifies mem_; 315 | requires 0 <= off < mem_.Length; 316 | requires mem_ != log_; 317 | requires ghost_state_inv(gs); 318 | requires ghost_state_equiv(gs); 319 | requires 0 <= off < gs.mem_len; 320 | ensures mem_ == old(mem_); 321 | ensures log_ == old(log_); 322 | ensures gs == old(gs); 323 | ensures ghost_state_equiv(mem_write_step(gs, off, val).0); 324 | { 325 | if (impl_countdown > 0) { 326 | mem_[off] := val; 327 | impl_countdown := impl_countdown - 1; 328 | } 329 | } 330 | 331 | method write_log(off : int, val : int) 332 | modifies this; 333 | modifies log_; 334 | requires 0 <= off <= |gs.log|; 335 | requires mem_ != log_; 336 | requires ghost_state_inv(gs); 337 | requires ghost_state_equiv(gs); 338 | requires off == 0 ==> 0 <= val * 2 < |gs.log|; 339 | ensures mem_ != log_; 340 | ensures mem_ == old(mem_); 341 | ensures log_ == old(log_); 342 | ensures log_.Length == old(log_).Length; 343 | ensures mem_[..] == old(mem_)[..]; 344 | ensures log_[off] == val || log_[off] == old(log_)[off]; 345 | ensures forall i :: 0 <= i < log_.Length && i != off ==> log_[i] == old(log_)[i]; 346 | ensures gs == old(gs); 347 | ensures off > 0 ==> ghost_state_equiv(log_write_step(gs, off - 1, val).0); 348 | ensures off == 0 ==> ghost_state_equiv(set_num_entry(gs, val).0); 349 | { 350 | if (impl_countdown > 0) { 351 | log_[off] := val; 352 | impl_countdown := impl_countdown - 1; 353 | } 354 | } 355 | 356 | method begin_tx() 357 | modifies log_; 358 | modifies this; 359 | requires state_inv(); 360 | requires ghost_state_equiv(gs); 361 | requires ghost_state_inv(gs); 362 | requires log_[0] == 0; 363 | ensures mem_ == old(mem_); 364 | ensures log_ == old(log_); 365 | ensures state_inv(); 366 | ensures ghost_state_equiv(gs); 367 | ensures ghost_tx_inv(gs); 368 | { 369 | write_log(0, 0); 370 | 371 | gs := ghost_begin_tx(gs); 372 | assert state_inv(); 373 | } 374 | 375 | method commit_tx() 376 | modifies log_; 377 | modifies this; 378 | requires state_inv(); 379 | requires ghost_state_equiv(gs); 380 | requires ghost_state_inv(gs); 381 | requires ghost_tx_inv(gs); 382 | requires old_mem_equiv(gs); 383 | ensures mem_ == old(mem_); 384 | ensures log_ == old(log_); 385 | ensures ghost_state_equiv(gs); 386 | ensures state_inv(); 387 | { 388 | write_log(0, 0); 389 | 390 | gs := ghost_commit_tx(gs).0; 391 | } 392 | 393 | method tx_write(offset: int, val : int) 394 | modifies this; 395 | modifies log_; 396 | modifies mem_; 397 | requires state_inv(); 398 | requires mem_ != log_; 399 | requires 0 <= offset < mem_.Length; 400 | requires ghost_state_equiv(gs); 401 | requires ghost_tx_inv(gs); 402 | requires old_mem_equiv(gs); 403 | requires 0 <= log_[0] * 2 + 3 < log_.Length; 404 | ensures ghost_state_equiv(gs); 405 | ensures ghost_tx_inv(gs); 406 | ensures old_mem_equiv(gs); 407 | { 408 | var log_idx := log_[0]; 409 | var log_off := log_idx * 2; 410 | ghost var old_gs := gs; 411 | write_log(log_off + 1, offset); 412 | gs := log_write_step(gs, log_off, offset).0; 413 | assert log_off + 1 > 0; 414 | assert ghost_state_equiv(gs); 415 | assert mem_ != log_; 416 | var old_val := mem_[offset]; 417 | assert old_val == gs.mem[offset]; 418 | write_log(log_off + 2, old_val); 419 | 420 | gs := log_write_step(gs, log_off + 1, old_val).0; 421 | 422 | assert ghost_tx_inv(gs); 423 | assert log_[0] == gs.num_entry; 424 | assert log_.Length == |gs.log| + 1; 425 | assert 0 <= gs.num_entry * 2 < |gs.log|; 426 | 427 | write_log(0, log_idx + 1); 428 | 429 | ghost var (s, f) := set_num_entry(gs, log_idx + 1); 430 | s := if f && !(offset in s.first_log_pos) 431 | then s.(first_log_pos := s.first_log_pos[offset := log_idx]) 432 | else s; 433 | gs := s; 434 | write_mem(offset, val); 435 | gs := mem_write_step(gs, offset, val).0; 436 | 437 | assert gs == ghost_tx_write(old_gs, offset, val); 438 | } 439 | 440 | // we assume that recover won't crash (though this code works when recover can fail) 441 | method recover() 442 | modifies log_; 443 | modifies mem_; 444 | modifies this; 445 | requires state_inv(); 446 | requires ghost_tx_inv(gs); 447 | requires old_mem_equiv(gs); 448 | requires ghost_state_equiv(gs); 449 | ensures gs == ghost_recover(old(gs)); 450 | ensures ghost_state_equiv(gs); 451 | { 452 | var log_len := log_[0]; 453 | assert log_len == gs.num_entry; 454 | if (log_len > 0) { 455 | var i := log_len - 1; 456 | 457 | ghost var gs0 := gs; 458 | while i >= 0 459 | modifies mem_; 460 | modifies this; 461 | invariant log_ == old(log_); 462 | invariant mem_ == old(mem_); 463 | invariant unchanged(log_); 464 | invariant -1 <= i < log_len; 465 | invariant |gs.log| == |gs0.log|; 466 | invariant ghost_state_equiv(gs); 467 | invariant ghost_tx_inv(gs); 468 | invariant old_mem_equiv(gs); 469 | invariant reverse_recovery(gs0, log_len) == reverse_recovery(gs, i + 1); 470 | decreases i; 471 | { 472 | assert ghost_state_equiv(gs); 473 | assert 0 <= i < log_[0]; 474 | var o := i * 2 + 1; 475 | var off := log_[o]; 476 | var val := log_[o + 1]; 477 | mem_[off] := val; 478 | assert 0 <= off < mem_.Length; 479 | 480 | assert gs.log[i * 2] == off; 481 | assert gs.log[i * 2 + 1] == val; 482 | gs := gs.(mem := gs.mem[off := val]); 483 | i := i - 1; 484 | } 485 | assert ghost_state_equiv(gs); 486 | } else { 487 | assert ghost_state_equiv(gs); 488 | } 489 | log_[0] := 0; 490 | gs := ghost_recover(old(gs)); 491 | assert ghost_state_equiv(gs); 492 | } 493 | } 494 | 495 | lemma crash_safe_single_tx(init_log : seq, init_mem : seq, 496 | countdown : int, 497 | writes : seq<(int, int)>) 498 | requires |init_log| > 0; 499 | requires countdown >= 0; 500 | requires forall i :: 0 <= i < |writes| ==> 501 | 0 <= writes[i].0 < |init_mem|; 502 | requires 0 < |writes| * 2 < |init_log|; 503 | { 504 | var s := init_ghost_state(init_log, init_mem, countdown); 505 | 506 | var end_mem := init_mem; 507 | 508 | s := ghost_begin_tx(s); 509 | assert s.num_entry == 0; 510 | assert init_mem == s.old_mem; 511 | 512 | var i := 0; 513 | while i < |writes| 514 | decreases |writes| - i; 515 | invariant 0 <= i <= |writes|; 516 | invariant s.mem_len == |init_mem|; 517 | invariant s.mem_len == |end_mem|; 518 | invariant 0 <= s.num_entry <= i; 519 | invariant |init_log| == |s.log|; 520 | invariant i * 2 < |s.log|; 521 | invariant 0 <= s.num_entry * 2 < |s.log|; 522 | invariant ghost_tx_inv(s); 523 | invariant old_mem_equiv(s); 524 | invariant init_mem == s.old_mem; 525 | invariant !crashed(s) ==> forall i :: 0 <= i < |s.mem| ==> s.mem[i] == end_mem[i]; 526 | { 527 | assert 0 <= i < |writes|; 528 | assert 0 <= writes[i].0 < s.mem_len; 529 | assert 0 <= s.num_entry * 2 + 2 < |s.log|; 530 | s := ghost_tx_write(s, writes[i].0, writes[i].1); 531 | 532 | end_mem := end_mem[writes[i].0 := writes[i].1]; 533 | 534 | assert !crashed(s) ==> s.mem[writes[i].0] == writes[i].1; 535 | i := i + 1; 536 | } 537 | 538 | assert ghost_tx_inv(s); 539 | assert old_mem_equiv(s); 540 | 541 | var (s', c) := ghost_commit_tx(s); 542 | assert c ==> !crashed(s); 543 | 544 | if (c) { 545 | assert !crashed(s); 546 | assert s.mem == end_mem; 547 | } else { 548 | var recovered := ghost_recover(s'); 549 | assert recovered.mem == init_mem; 550 | } 551 | } --------------------------------------------------------------------------------