Skip to content

Commit

Permalink
architecture-agnostic injection supported
Browse files Browse the repository at this point in the history
- issue #81 partially resolved - x64/x86 processes can be now attached to;
- injector functionality is moved to a separate DLL - an ipc server ran via sandbox;
- injector DLLs unloads itself upon completion;
- vsix installers updated;
- injector/process class is no longer an interface.
  • Loading branch information
tyoma committed Aug 8, 2022
1 parent 0a3ad74 commit 624b918
Show file tree
Hide file tree
Showing 29 changed files with 588 additions and 185 deletions.
6 changes: 6 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,13 @@ option(MP_BUILD_VSPACKAGE "Build Visual Studio Package." ON)

set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${PROJECT_SOURCE_DIR}/build.props)

include(architecture)
include(unipath)

set(injector "micro-profiler_injector_${archid}")
set(micro-profiler "micro-profiler_${archid}")
set(sandbox "micro-profiler_sandbox_${archid}")

enable_testing()


Expand Down Expand Up @@ -149,6 +154,7 @@ if (WIN32 OR APPLE)
if (WIN32)
add_subdirectory(explorer/src)
add_subdirectory(injector/src)
add_subdirectory(sandbox/src)

if (MP_BUILD_VSPACKAGE)
add_subdirectory(libraries/wpl.vs)
Expand Down
12 changes: 12 additions & 0 deletions build-vsix.cmd
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,16 @@ pushd "%~dp0_build.windows.x86\_bin"

call sha256 hashmpx86 micro-profiler_Win32.dll
call sha256 hashmpx86lib micro-profiler_Win32.lib

call sha256 hashsbxx86 micro-profiler_sandbox_Win32.exe
call sha256 hashinjx86 micro-profiler_injector_Win32.dll
popd
pushd "%~dp0_build.windows.x64\_bin"
call sha256 hashmpx64 micro-profiler_x64.dll
call sha256 hashmpx64lib micro-profiler_x64.lib

call sha256 hashsbxx64 micro-profiler_sandbox_x64.exe
call sha256 hashinjx64 micro-profiler_injector_x64.dll
popd
pushd "%~dp0_build.linux.x86\_bin"
call sha256 hashlinuxmpx86 libmicro-profiler_x86.so
Expand Down Expand Up @@ -68,10 +74,16 @@ pushd "%~dp0_build.windows.x86\_bin"

call mkzip micro-profiler_Win32.dll "%OUTPUT%"
call mkzip micro-profiler_Win32.lib "%OUTPUT%"

call mkzip micro-profiler_sandbox_Win32.exe "%OUTPUT%"
call mkzip micro-profiler_injector_Win32.dll "%OUTPUT%"
popd
pushd "%~dp0_build.windows.x64\_bin"
call mkzip micro-profiler_x64.dll "%OUTPUT%"
call mkzip micro-profiler_x64.lib "%OUTPUT%"

call mkzip micro-profiler_sandbox_x64.exe "%OUTPUT%"
call mkzip micro-profiler_injector_x64.dll "%OUTPUT%"
popd
pushd "%~dp0_build.linux.x86\_bin"
call mkzip libmicro-profiler_x86.so "%OUTPUT%"
Expand Down
10 changes: 10 additions & 0 deletions build-vsix.x64.cmd
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ popd
pushd "%~dp0_build.windows.x86\_bin"
call sha256 hashmpx86 micro-profiler_Win32.dll
call sha256 hashmpx86lib micro-profiler_Win32.lib

call sha256 hashsbxx86 micro-profiler_sandbox_Win32.exe
call sha256 hashinjx86 micro-profiler_injector_Win32.dll
popd
pushd "%~dp0_build.windows.x64\_bin"
copy /y extension.x64.vsixmanifest extension.vsixmanifest
Expand All @@ -32,6 +35,9 @@ pushd "%~dp0_build.windows.x64\_bin"

call sha256 hashmpx64 micro-profiler_x64.dll
call sha256 hashmpx64lib micro-profiler_x64.lib

call sha256 hashsbxx64 micro-profiler_sandbox_x64.exe
call sha256 hashinjx64 micro-profiler_injector_x64.dll
popd
pushd "%~dp0_build.linux.x86\_bin"
call sha256 hashlinuxmpx86 libmicro-profiler_x86.so
Expand Down Expand Up @@ -63,6 +69,8 @@ popd
pushd "%~dp0_build.windows.x86\_bin"
call mkzip micro-profiler_Win32.dll "%OUTPUT%"
call mkzip micro-profiler_Win32.lib "%OUTPUT%"
call mkzip micro-profiler_sandbox_Win32.exe "%OUTPUT%"
call mkzip micro-profiler_injector_Win32.dll "%OUTPUT%"
popd
pushd "%~dp0_build.windows.x64\_bin"
call mkzip micro-profiler_frontend.dll "%OUTPUT%"
Expand All @@ -74,6 +82,8 @@ pushd "%~dp0_build.windows.x64\_bin"

call mkzip micro-profiler_x64.dll "%OUTPUT%"
call mkzip micro-profiler_x64.lib "%OUTPUT%"
call mkzip micro-profiler_sandbox_x64.exe "%OUTPUT%"
call mkzip micro-profiler_injector_x64.dll "%OUTPUT%"
popd
pushd "%~dp0_build.linux.x86\_bin"
call mkzip libmicro-profiler_x86.so "%OUTPUT%"
Expand Down
13 changes: 13 additions & 0 deletions build.props/architecture.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
if(NOT ANDROID_ABI OR ANDROID_ABI MATCHES "x86.*")
if(CMAKE_SIZEOF_VOID_P EQUAL 4)
if(WIN32)
set(archid "Win32")
else()
set(archid "x86")
endif()
else()
set(archid "x64")
endif()
elseif(ANDROID_ABI MATCHES "arm.*")
set(archid "arm")
endif()
14 changes: 0 additions & 14 deletions collector/src/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,19 +1,5 @@
cmake_minimum_required(VERSION 3.4)

if(NOT ANDROID_ABI OR ANDROID_ABI MATCHES "x86.*")
if(CMAKE_SIZEOF_VOID_P EQUAL 4)
if(WIN32)
set(micro-profiler "micro-profiler_Win32")
else()
set(micro-profiler "micro-profiler_x86")
endif()
else()
set(micro-profiler "micro-profiler_x64")
endif()
elseif(ANDROID_ABI MATCHES "arm.*")
set(micro-profiler "micro-profiler_arm")
endif()

set(MP_OUTDIR $<TARGET_FILE_DIR:${micro-profiler}>)

if(MSVC)
Expand Down
1 change: 1 addition & 0 deletions deployment/vsix/[Content_Types].xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">
<Default Extension="vsixmanifest" ContentType="text/xml" />
<Default Extension="dll" ContentType="application/octet-stream" />
<Default Extension="exe" ContentType="application/octet-stream" />
<Default Extension="so" ContentType="application/octet-stream" />
<Default Extension="lib" ContentType="application/octet-stream" />
<Default Extension="ico" ContentType="application/octet-stream" />
Expand Down
5 changes: 5 additions & 0 deletions deployment/vsix/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@
{"fileName":"/libmicro-profiler_x86.so","sha256":"!hashlinuxmpx86!"},
{"fileName":"/libmicro-profiler_x64.so","sha256":"!hashlinuxmpx64!"},

{"fileName":"/micro-profiler_sandbox_Win32.exe","sha256":"!hashsbxx86!"},
{"fileName":"/micro-profiler_sandbox_x64.exe","sha256":"!hashsbxx64!"},
{"fileName":"/micro-profiler_injector_Win32.dll","sha256":"!hashinjx86!"},
{"fileName":"/micro-profiler_injector_x64.dll","sha256":"!hashinjx64!"},

{"fileName":"/windows7+/x64/api-ms-win-downlevel-kernel32-l2-1-0.dll","sha256":"!hashkernel32downlevelapix64!"},
{"fileName":"/windows7+/x64/dbghelp.dll","sha256":"!hashdbghelpx64!"},
{"fileName":"/windows7+/x86/api-ms-win-downlevel-kernel32-l2-1-0.dll","sha256":"!hashkernel32downlevelapix86!"},
Expand Down
4 changes: 4 additions & 0 deletions deployment/wix/micro-profiler.wxs
Original file line number Diff line number Diff line change
Expand Up @@ -142,8 +142,12 @@
<Component Id="compCollectorWindows" Guid="47674731-7CDF-4D96-ABE6-BC85BCA68161" KeyPath="yes" Directory="TARGETDIR">
<?ifdef SOURCEDIRWX64?>
<File Id="fileCollectorDLL_x64" Source="$(var.SOURCEDIRWX64)/micro-profiler_x64.dll" Vital="yes" />
<File Id="fileInjectorDLL_x64" Source="$(var.SOURCEDIRWX64)/micro-profiler_injector_x64.dll" Vital="yes" />
<File Id="fileSandbox_x64" Source="$(var.SOURCEDIRWX64)/micro-profiler_sandbox_x64.exe" Vital="yes" />
<?endif?>
<File Id="fileCollectorDLL_x86" Source="$(var.SOURCEDIRWX86)/micro-profiler_Win32.dll" Vital="yes" />
<File Id="fileInjectorDLL_x86" Source="$(var.SOURCEDIRWX86)/micro-profiler_injector_Win32.dll" Vital="yes" />
<File Id="fileSandbox_x86" Source="$(var.SOURCEDIRWX86)/micro-profiler_sandbox_Win32.exe" Vital="yes" />
</Component>
<?ifdef SOURCEDIRLINUXX86?>
<Component Id="compCollectorLinuxX86" Guid="5A9890DA-1B8D-48EA-986D-8DB26073B79C" KeyPath="yes" Directory="TARGETDIR">
Expand Down
6 changes: 6 additions & 0 deletions explorer/src/process_win32.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,12 @@ namespace micro_profiler
{
if (auto handle = ::OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, entry.th32ProcessID))
{
BOOL wow64 = FALSE;

// TODO: untested
if (::IsWow64Process(handle, &wow64))
p.architecture = wow64 ? process_info::x86 : process_info::x64;

p.handle.reset(handle, &::CloseHandle);
}
else
Expand Down
52 changes: 52 additions & 0 deletions injector/injector.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// Copyright (c) 2011-2022 by Artem A. Gevorkyan (gevorkyan.org)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

#pragma once

#include <string>

namespace micro_profiler
{
enum injector_messages_id {
request_injection,
response_injected,
};

struct injection_info
{
std::string collector_path;
std::string frontend_endpoint_id;
unsigned int pid;
};



template <typename ArchiveT>
inline void serialize(ArchiveT &archive, injection_info &info)
{
archive(info.collector_path);
archive(info.frontend_endpoint_id);
archive(info.pid);
}

template <typename ArchiveT>
inline void serialize(ArchiveT &archive, injector_messages_id &data)
{ archive(reinterpret_cast<int &>(data)); }
}
18 changes: 13 additions & 5 deletions injector/process.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,20 +20,28 @@

#pragma once

#include <common/noncopyable.h>
#include <common/range.h>
#include <memory>
#include <string>

namespace micro_profiler
{
struct process
class process : noncopyable
{
public:
typedef void (injection_function_t)(const_byte_range payload);

virtual unsigned get_pid() const = 0;
virtual std::string name() const = 0;
virtual void remote_execute(injection_function_t *injection_function, const_byte_range payload) = 0;
public:
process(unsigned int pid);
~process();

static std::shared_ptr<process> open(unsigned int pid);
void remote_execute(injection_function_t *injection_function, const_byte_range payload);

private:
class impl;

private:
impl *_impl;
};
}
13 changes: 10 additions & 3 deletions injector/src/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
cmake_minimum_required(VERSION 2.8)

set(INJECTOR_SOURCES
add_definitions(-DMP_EXPORT_PREFIX=__declspec\(dllexport\))

set(INJECTOR_LIB_SOURCES
process_win32.cpp
)
set(INJECTOR_SOURCES
injector.cpp
)

add_library(injector STATIC ${INJECTOR_SOURCES})
add_library(injector.lib STATIC ${INJECTOR_LIB_SOURCES})
add_library(${injector} SHARED ${INJECTOR_SOURCES})

target_link_libraries(injector psapi.lib)
target_link_libraries(injector.lib psapi.lib)
target_link_libraries(${injector} injector.lib ipc common logger scheduler)
Original file line number Diff line number Diff line change
Expand Up @@ -18,68 +18,53 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

#include <micro-profiler/frontend/inject_profiler.h>
#include <injector/injector.h>

#include <common/constants.h>
#include <common/module.h>
#include <common/path.h>
#include <common/pod_vector.h>
#include <common/range.h>
#include <common/stream.h>
#include <injector/process.h>
#include <ipc/endpoint_spawn.h>
#include <ipc/server_session.h>
#include <sandbox/sandbox.h>
#include <strmd/deserializer.h>
#include <strmd/packer.h>
#include <strmd/serializer.h>

using namespace micro_profiler;
using namespace micro_profiler::ipc;
using namespace std;

extern "C" int setenv(const char *name, const char *value, int overwrite);

namespace micro_profiler
namespace
{
namespace
void inject_profiler_worker(const_byte_range payload)
{
#ifdef _M_IX86
const string c_profiler_module_name = "micro-profiler_Win32.dll";
#elif _M_X64
const string c_profiler_module_name = "micro-profiler_x64.dll";
#endif
buffer_reader r(payload);
strmd::deserializer<buffer_reader, strmd::varint> d(r);
injection_info injection;

struct parameters
{
string frontend_id;
};

template <typename ArchiveT>
void serialize(ArchiveT &archive, parameters &data)
{
archive(data.frontend_id);
}

void inject_profiler_worker(const_byte_range payload)
{
buffer_reader r(payload);
strmd::deserializer<buffer_reader, strmd::varint> d(r);
parameters p;
const auto dir = ~get_module_info(&inject_profiler).path;

d(p);
setenv(constants::frontend_id_ev, p.frontend_id.c_str(), 1);
setenv(constants::profiler_injected_ev, "1", 1);
load_library(dir & c_profiler_module_name); // No need to hold the handle, as profiler pins to the process.
}
d(injection);
setenv(constants::frontend_id_ev, injection.frontend_endpoint_id.c_str(), 1);
setenv(constants::profiler_injected_ev, "1", 1);
load_library(injection.collector_path); // No need to hold the handle, as profiler pins to the process.
}
}

void inject_profiler(process &process_, const std::string &frontend_id)
{
extern "C" MP_EXPORT_PREFIX void ipc_spawn_server(micro_profiler::ipc::channel_ptr_t &session_,
const vector<string> &/*arguments*/, micro_profiler::ipc::channel &outbound)
{
const auto session = make_shared<server_session>(outbound);

session->add_handler(request_injection, [] (server_session::response &response, const injection_info &injection) {
auto p = make_shared<process>(injection.pid);
pod_vector<byte> buffer;
buffer_writer< pod_vector<byte> > w(buffer);
strmd::serializer<buffer_writer< pod_vector<byte> >, strmd::varint> s(w);
parameters p = {
frontend_id,
};

s(p);
process_.remote_execute(&inject_profiler_worker, const_byte_range(buffer.data(), buffer.size()));
}
s(injection);
p->remote_execute(&inject_profiler_worker, const_byte_range(buffer.data(), buffer.size()));
response(response_injected);
});
session_ = session;
}
Loading

0 comments on commit 624b918

Please sign in to comment.