| /* |
| * Copyright (C) 2016 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #ifndef GAPII_SPY_BASE_H |
| #define GAPII_SPY_BASE_H |
| |
| #include "abort_exception.h" |
| #include "call_observer.h" |
| #include "return_handler.h" |
| #include "slice.h" |
| |
| #include <gapic/scratch_allocator.h> |
| #include <gapic/encoder.h> |
| #include <gapic/interval_list.h> |
| #include <gapic/mutex.h> |
| #include <gapic/thread_local.h> |
| #include <gapic/vector.h> |
| |
| #include <gapic/coder/memory.h> |
| #include <gapic/coder/atom.h> |
| |
| #include <stdint.h> |
| |
| #include <memory> |
| #include <string> |
| #include <unordered_set> |
| |
| namespace gapii { |
| |
| class SpyBase { |
| public: |
| SpyBase(); |
| |
| void init(CallObserver* observer, std::shared_ptr<gapic::Encoder> encoder); |
| |
| // lock begins the interception of a single command. It must be called |
| // before invoking any command on the spy. Blocks if any other thread |
| // is has called lock and not yet called unlock. |
| void lock(CallObserver* observer, const char* name); |
| |
| // unlock must be called after invoking any command. |
| // resets the buffers reused between atoms. |
| void unlock(); |
| |
| // Set whether to observe the application pool. If true, the default, |
| // then reads and writes to the application pools are observed, but |
| // writes do not change the memory contents. If false, then |
| // no-observations are made and writes change the application memory. |
| inline void setObserveApplicationPool(bool observeApplicationPool); |
| // Set whether to just pass forward the calls directly from the |
| // interceptor to the underlying driver. If false, the default, then |
| // API state logic is run and an atom is emitted to the trace stream. |
| // If true, the spy is just a trampoline. |
| inline void setPassthrough(bool passthrough); |
| // Set the handler to use when handle() is called with an abort |
| // exception. This allows different treatment for different |
| // aborts without requiring API specific knowledge. |
| typedef std::function<void(CallObserver* observer, const AbortException&)> AbortHandler; |
| inline void setHandler(AbortHandler handler); |
| // Returns true if the spy should compute the expected return value and |
| // call setExpectedReturn(v). Default is false. |
| inline bool shouldComputeExpectedReturn() const; |
| // Set the handler to use when setExpectedReturn(val) is called. |
| // By default the return value specified is ignored. |
| inline void setReturnHandler(std::shared_ptr<ReturnHandler> handler); |
| |
| // Returns the set of resources ids. |
| // TODO(qining): To support multithreaded uses, mutex is required to manage |
| // the access to this set. |
| std::unordered_set<gapic::Id>& getResources() { return mResources; } |
| // Returns the transimission encoder. |
| // TODO(qining): To support multithreaded uses, mutex is required to manage |
| // the access to this encoder. |
| std::shared_ptr<gapic::Encoder> getEncoder() { return mEncoder; } |
| // Returns true if we should observe application pool. |
| bool shouldObserveApplicationPool() { return mObserveApplicationPool; } |
| |
| // Tries to enter this function. If SpyBase has already been entered before |
| // by the same thread, this returns false. e.g. If the driver calls the |
| // function recursively. |
| bool try_to_enter(); |
| |
| // Leaves this function. Only valid to call whenever we have succeeded |
| // at a call of try_to_enter. |
| void exit(); |
| |
| protected: |
| static const size_t kMaxExtras = 16; // Per atom |
| |
| typedef gapic::coder::atom::Observation Observation; |
| |
| typedef std::unordered_set<gapic::Id> IdSet; |
| typedef std::shared_ptr<gapic::Encoder> EncoderSPtr; |
| |
| // onThreadSwitched is invoked by enter() whenever the current thread changes. |
| virtual void onThreadSwitched(CallObserver* observer, uint64_t threadID) = 0; |
| |
| // make constructs and returns a Slice backed by a new pool. |
| template<typename T> |
| inline Slice<T> make(uint64_t count) const; |
| |
| // slice returns a slice wrapping the application-pool pointer src, starting at elements s |
| // ending at one element before e. |
| template<typename T> |
| inline Slice<T> slice(T* src, uint64_t s, uint64_t e) const; |
| |
| // slice returns a slice wrapping the application-pool pointer src, starting at s bytes |
| // from src and ending at one byte before e. |
| inline Slice<uint8_t> slice(void* src, uint64_t s, uint64_t e) const; |
| |
| // slice returns a Slice<char>, backed by a new pool, holding a copy of the string src. |
| // src is observed as a read operation. |
| inline Slice<char> slice(const std::string& src) const; |
| |
| // slice returns a sub-slice of src, starting at elements s and ending at one element before e. |
| template<typename T> |
| inline Slice<T> slice(const Slice<T>& src, uint64_t s, uint64_t e) const; |
| |
| // abort signals that the atom should stop execution immediately. |
| void abort(); |
| |
| // handle is called in the abort exception catch. Used to allow |
| // customization of abort handling. |
| inline void handleAbort(CallObserver* observer, const AbortException& e); |
| |
| // onPostDrawCall is after any command annotated with @DrawCall |
| inline virtual void onPostDrawCall() {} |
| |
| // onPreEndOfFrame is before any command annotated with @EndOfFrame |
| inline virtual void onPreEndOfFrame() {} |
| |
| // onPostFence is called immediately after the driver call. |
| inline virtual void onPostFence(CallObserver* observer) {} |
| |
| // Abort handler used when if no other handler has been specified |
| void defaultAbortHandler(CallObserver* observer, const AbortException& e); |
| |
| // Returns true if the current thread is currently "in" the spy, where |
| // "in" is defined as "the time between a true return of try_to_enter and |
| // a matching call to exit". |
| bool has_entered() { |
| return mReentrantFlag.get() != 0; |
| } |
| |
| // The output stream encoder. |
| EncoderSPtr mEncoder; |
| |
| // If true the interceptor calls the underlying function directly. |
| bool mPassthrough; |
| |
| // A counter that is incremented each time a graphics command starts or |
| // ends. The first command start gets a value of 0 for its starting command |
| // counter value. |
| uint64_t mCommandStartEndCounter; |
| |
| // The expected counter value for the starting of the next command. This |
| // equals the counter value of the last command ending plus one. This value |
| // starts at 0 before any atoms have been sent. |
| uint64_t mExpectedNextCommandStartCounterValue; |
| |
| // Stores the extra if the command aborted. |
| gapic::coder::atom::Aborted* mAborted; |
| |
| // Used by the generated code to indicate that the API file compute t as the |
| // return for this call. The actual return value comes from the driver. |
| template <typename T> void setExpectedReturn(const T& t); |
| private: |
| template <class T> bool shouldObserve(const Slice<T>& slice) const; |
| |
| // The list of observations that have already been encoded. |
| IdSet mResources; |
| |
| // The current thread ID. |
| uint64_t mCurrentThread; |
| |
| // The mutex that should be locked for the duration of each of the intercepted commands. |
| gapic::Mutex mMutex; |
| // True if we should observe the application pool. |
| bool mObserveApplicationPool; |
| |
| // If non-null this handler is used instead of defaultAbortHandler. |
| AbortHandler mAbortHandler; |
| |
| // If non-null this is a class which can accept return values of arbitrary |
| // types (with a copy constructor and assignment operator). |
| std::shared_ptr<ReturnHandler> mReturnHandler; |
| |
| // Initially set to zero for all threads. This is set to a non-zero value |
| // for every thread that calls try_to_enter with a true return value, |
| // and reset for that thread when the matching exit() function is called. |
| gapic::ThreadLocalValue mReentrantFlag; |
| |
| }; |
| |
| // finds a key in the map and returns the value. If no value is present |
| // returns the zero for that type. |
| template<typename Map> |
| const typename Map::mapped_type& findOrZero(const Map& m, const typename Map::key_type& key) { |
| auto it = m.find(key); |
| if (it == m.end()) { |
| static auto zero = typename Map::mapped_type(); |
| return zero; |
| } |
| return it->second; |
| } |
| |
| inline bool SpyBase::shouldComputeExpectedReturn() const { |
| return !mObserveApplicationPool && mReturnHandler != nullptr; |
| } |
| |
| inline void SpyBase::setReturnHandler(std::shared_ptr<ReturnHandler> handler) { |
| mReturnHandler = handler; |
| } |
| |
| template <typename T> |
| void SpyBase::setExpectedReturn(const T& t) { |
| spyAssert(shouldComputeExpectedReturn(), "setExpectedReturn called, but shouldComputeExpectedReturn is false"); |
| mReturnHandler->setReturnValue(t); |
| } |
| |
| template <class T> |
| bool SpyBase::shouldObserve(const Slice<T>& slice) const { |
| return mObserveApplicationPool && slice.isApplicationPool(); |
| } |
| |
| inline void SpyBase::setObserveApplicationPool(bool observeApplicationPool) { |
| mObserveApplicationPool = observeApplicationPool; |
| } |
| |
| inline void SpyBase::setPassthrough(bool passthrough) { |
| mPassthrough = passthrough; |
| } |
| |
| inline void SpyBase::setHandler(AbortHandler handler) { |
| mAbortHandler = handler; |
| } |
| |
| inline void SpyBase::handleAbort(CallObserver* observer, const AbortException& e) { |
| if (mAbortHandler == nullptr) { |
| defaultAbortHandler(observer, e); |
| } else { |
| mAbortHandler(observer, e); |
| } |
| } |
| |
| template<typename T> |
| inline Slice<T> SpyBase::make(uint64_t count) const { |
| auto pool = Pool::create(count * sizeof(T)); |
| return Slice<T>(reinterpret_cast<T*>(pool->base()), count, pool); |
| } |
| |
| template<typename T> |
| inline Slice<T> SpyBase::slice(T* src, uint64_t s, uint64_t e) const { |
| // TODO: Find the pool containing src |
| return Slice<T>(src+s, e-s, std::shared_ptr<Pool>()); |
| } |
| |
| inline Slice<uint8_t> SpyBase::slice(void* src, uint64_t s, uint64_t e) const { |
| return slice(reinterpret_cast<uint8_t*>(src), s, e); |
| } |
| |
| inline Slice<char> SpyBase::slice(const std::string& src) const { |
| Slice<char> dst = make<char>(src.length()); |
| for (uint64_t i = 0; i < src.length(); i++) { |
| dst[i] = src[i]; |
| } |
| return dst; |
| } |
| |
| template<typename T> |
| inline Slice<T> SpyBase::slice(const Slice<T>& src, uint64_t s, uint64_t e) const { |
| return src(s, e); |
| } |
| |
| } // namespace gapii |
| |
| #endif // GAPII_SPY_BASE_H |