Is it possible to get class name when implementing malloc_logger function in iOS? - ios

From here, we know if malloc_logger global function is defined, it will be called whenever there is a malloc or free operation. I want to use it to record memory allocations in my app like this:
typedef void(malloc_logger_t)(uint32_t type,
uintptr_t arg1,
uintptr_t arg2,
uintptr_t arg3,
uintptr_t result,
uint32_t num_hot_frames_to_skip);
extern malloc_logger_t *malloc_logger;
void my_malloc_stack_logger(uint32_t type, uintptr_t arg1, uintptr_t arg2, uintptr_t arg3, uintptr_t result, uint32_t num_hot_frames_to_skip);
malloc_logger = my_malloc_stack_logger;
void my_malloc_stack_logger(uint32_t type, uintptr_t arg1, uintptr_t arg2, uintptr_t arg3, uintptr_t result, uint32_t num_hot_frames_to_skip)
{
// do my work
}
In my_malloc_stack_logger, I can directly get the allocated size and address. But how about object types? I want to record the class name if it is an NSObject instance. Is it possible to get this information?

After playing around with the hook, it looks like what you want to achieve is not quite possible.
First problem here is that if you try to read a class name from within this function (by calling any of object_getClassName, class_getName
or NSStringFromClass), this action on its own tends to trigger new allocations. That apparently happens because some Cocoa classes load lazily. I noticed however that when requesting all classes with objc_getClassList it makes a lot of preliminary allocations that helps to avoid them later on. So my idea is to cache all class names before subscribing to the allocations hook and refer to the cached values when needed. For the storage I used Apple's CFMutableDictionary:
CFMutableDictionaryRef objc_class_records;
void refresh_objc_class_list(void) {
pthread_mutex_lock(&objc_class_records_mutex);
if (objc_class_records) {
CFRelease(objc_class_records);
}
objc_class_records = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, NULL, &kCFTypeDictionaryValueCallBacks);
// The buffer needs to accomodate at least 26665 instances
static const unsigned buffer_length = 100000;
Class registered_classes[buffer_length];
objc_getClassList(registered_classes, buffer_length);
for (unsigned i = 0; i < buffer_length; ++i) {
if (!registered_classes[i]) {
break;
}
const Class class = registered_classes[i];
const CFStringRef class_name = CFStringCreateWithCString(kCFAllocatorDefault, class_getName(class), kCFStringEncodingUTF8);
CFDictionarySetValue(objc_class_records, class, class_name);
CFRelease(class_name);
}
}
Be advised that you don't want to have it called when the malloc logger is enabled (especially from within the hook itself).
Now you need to obtain a Class instance from the Objective-C objects. Depending on the type of allocation, the pointer argument goes to fifth or third parameter:
void my_malloc_logger(uint32_t type, uintptr_t param0, uintptr_t param1, uintptr_t param2,
uintptr_t param3, uint32_t frames_to_skip) {
void *ptr = NULL;
unsigned size = 0;
switch (type) {
case MALLOC_OP_MALLOC:
case MALLOC_OP_CALLOC:
ptr = (void *)param3;
size = (unsigned)param1;
break;
case MALLOC_OP_REALLOC:
ptr = (void *)param3;
size = (unsigned)param2;
break;
case MALLOC_OP_FREE:
ptr = (void *)param1;
break;
}
id objc_ptr = (id)ptr;
Class objc_class = object_getClass(objc_ptr);
if (!objc_class) {
return;
}
const CFStringRef class_name;
const bool found = CFDictionaryGetValueIfPresent(objc_class_records, objc_class, (const void **)&class_name);
if (found) {
const static unsigned name_max_length = 256;
char c_class_name[name_max_length];
if (CFStringGetCString(class_name, c_class_name, name_max_length, kCFStringEncodingUTF8)) {
const char *alloc_name = alloc_type_name(type);
nomalloc_printf_sync("%7s: Pointer: %p; Size: %u; Obj-C class: \"%s\"\n", alloc_name, objc_ptr, size, c_class_name);
}
}
}
And now why it won't work as expected:
object_getClass is not able to tell whether a pointer is an object of Cococa classes at the time of allocation (it will find the class, however, when the object is already allocated, e.g. before deallocation). Thus, the following code:
[NSObject new];
Will produce output similar to this:
CALLOC: Pointer: 0x600000600080; Size: 16
FREE: Pointer: 0x600000600080; Size: 0; Obj-C class: "NSObject"
Most of the standard Cocoa classes are in fact so-called Class Clusters and under the hood the actual allocation happens for an instance of a private class (which is not always recognisable by its public interface), thus this information is incomplete and sometimes misleading.
There are also many other factors which need to be taken into account (which i didn't cover here because it's beyond the question asked): the way you output data to standard output should not cause allocation by itself; the logging needs synchronisation since allocation happens a lot from any number of threads; if you want to enable/disable recording the Objective-C classes (or update the cache occasionally) access to the storage also needs to be synchronised.
Having that said if you are satisfied with what can be done with it, feel free to refer to the repository I made where this approach is already implemented in a form of a static library.

Related

store a lambda that captures this

Using C++ 17, I'm looking for a way to store a lambda that captures the this pointer, without using std::function<>. The reason to not using std::function<> is that I need the guaranty that no dynamic memory allocations are used. The purpose of this, is to be able to define some asynchronous program flow. Example:
class foo {
public:
void start() {
timer(1ms, [this](){
set_pin(1,2);
timer(1ms, [this](){
set_pin(2,1);
}
}
}
private:
template < class Timeout, class Callback >
void timer( Timeout to, Callback&& cb ) {
cb_ = cb;
// setup timer and call cb_ one timeout reached
...
}
??? cb_;
};
Edit: Maybe it's not really clear: std::function<void()> would do the job, but I need / like to have the guaranty, that no dynamic allocations happens as the project is in the embedded field. In practice std::function<void()> seems to not require dynamic memory allocation, if the lambda just captures this. I guess this is due to some small object optimizations, but I would like to not rely on that.
You can write your own function_lite to store the lambda, then you can use static_assert to check the size and alignment requirements are satisfied:
#include <cstddef>
#include <new>
#include <type_traits>
class function_lite {
static constexpr unsigned buffer_size = 16;
using trampoline_type = void (function_lite::*)() const;
trampoline_type trampoline;
trampoline_type cleanup;
alignas(std::max_align_t) char buffer[buffer_size];
template <typename T>
void trampoline_func() const {
auto const obj =
std::launder(static_cast<const T*>(static_cast<const void*>(buffer)));
(*obj)();
}
template <typename T>
void cleanup_func() const {
auto const obj =
std::launder(static_cast<const T*>(static_cast<const void*>(buffer)));
obj->~T();
}
public:
template <typename T>
function_lite(T t)
: trampoline(&function_lite::trampoline_func<T>),
cleanup(&function_lite::cleanup_func<T>) {
static_assert(sizeof(T) <= buffer_size);
static_assert(alignof(T) <= alignof(std::max_align_t));
new (static_cast<void*>(buffer)) T(t);
}
~function_lite() { (this->*cleanup)(); }
function_lite(function_lite const&) = delete;
function_lite& operator=(function_lite const&) = delete;
void operator()() const { (this->*trampoline)(); }
};
int main() {
int x = 0;
function_lite f([x] {});
}
Note: this is not copyable; to add copy or move semantics you will need to add new members like trampoline and cleanup which can properly copy the stored object.
There is no drop in replacement in the language or the standard library.
Every lambda is a unique type in the typesystem. Technically you may have a lambda as a member, but then its type is fixed. You may not assign other lambdas to it.
If you really want to have an owning function wrapper like std::function, you need to write your own. Actually you want a std::function with a big enough small-buffer-optimization buffer.
Another approach would be to omit the this capture and pass it to the function when doing the call. So you have a captureless lambda, which is convertible to a function pointer which you can easily store. I would take this route and adapter complexer ways if really nessessary.
it would look like this (i trimmed down the code a bit):
class foo
{
public:
void start()
{
timer(1, [](foo* instance)
{
instance->set_pin(1,2);
});
}
private:
template < class Timeout, class Callback >
void timer( Timeout to, Callback&& cb )
{
cb_ = cb;
cb_(this); // call the callback like this
}
void set_pin(int, int)
{
std::cout << "pin set\n";
}
void(*cb_)(foo*);
};

passing an ofstream object to a thread function via pthread_create

I want to pass an ofstream object to a thread using pthread_create function.
Let's say I create an object like this in my main(int argc, char *argv[]) function.
ofstream file1(argv[1], fstream::out|fstream::app);
pthread_t tid;
pthread_create(&tid, NULL, function1, (void *)args);
And the function1 is defined as.
void function1(void *input)
{
ofstream file;
file = ??
file << "Hello" << endl;
}
How can I access "file1.txt" created in the main function via the file object?
There are several ways to pass the object. One would be to make it global, then then it's very straightforward for function1() to access it. Or you can pass a pointer to it as the thread's argument. Cast it to void* in the pthread_create() call and cast it back in the thread function.
void* function1(void *input) {
ofstream* file = static_cast<ofstream*>(input);
*file << "Hello" << endl;
return NULL;
}
ofstream file1("./file1.txt", fstream::out|fstream::app);
pthread_t tid;
pthread_create(&tid, NULL, function1, static_cast<void*>(&file1));
Be warned, there is a common bug that this pattern often leads to! The ofstream will be destructed when the scope it was created in ends, which is in the thread that called pthread_create(). If the thread running function1() is still running, it could use the pointer to the now destructed ofstream.
You need to insure that the ofstream remains alive until the other thread is done with it. One way would be to give it static storage duration, either as a static local variable or as a global variable. Another would to allocate it with new and then delete it in the thread that is using it. Or you could insure that the created thread is joined before the ifstream leaves scope.
Using new and delete:
void start(void) {
ofstream* file1 = new ofstream("file1.txt", fstream::app);
pthread_create(&tid, NULL, function1, static_cast<void*>(file1));
file1 = NULL; // function1 owns this now, shouldn't be used here anymore
}
void* function1(void* arg) {
ofstream* file1 = static_cast<ofstream*>(arg);
delete file1;
}
Joining before the ofstream leaves scope:
void start(void) {
{
ofstream file1("file1.txt", fstream::app);
pthread_create(&tid, NULL, function1, static_cast<void*>(file1));
// stuff
pthread_join(tid);
// now it's safe for file1 to be destructed.
}
}
Also note that the thread function should return void*, not void. Also it should be declared as extern "C" so that it will have the correct ABI when the C pthreads library calls it. Notice how in the new/delete example, I set the pointer to NULL after starting the thread. This is because you can not assume it is safe to access the ofstream from more than one thread at a time. By setting it to NULL, only the function1 thread will be able to access it.
Consider also, maybe it makes more sense to pass the name to the thread and have it open the file? This solves the issues around the lifetime of the object vs the lifetime of the thread using it.

FatalExecutionEngineError on accessing a pointer set with memcpy_s

See update 1 below for my guess as to why the error is happening
I'm trying to develop an application with some C#/WPF and C++. I am having a problem on the C++ side on a part of the code that involves optimizing an object using GNU Scientific Library (GSL) optimization functions. I will avoid including any of the C#/WPF/GSL code in order to keep this question more generic and because the problem is within my C++ code.
For the minimal, complete and verifiable example below, here is what I have. I have a class Foo. And a class Optimizer. An object of class Optimizer is a member of class Foo, so that objects of Foo can optimize themselves when it is required.
The way GSL optimization functions take in external parameters is through a void pointer. I first define a struct Params to hold all the required parameters. Then I define an object of Params and convert it into a void pointer. A copy of this data is made with memcpy_s and a member void pointer optimParamsPtr of Optimizer class points to it so it can access the parameters when the optimizer is called to run later in time. When optimParamsPtr is accessed by CostFn(), I get the following error.
Managed Debugging Assistant 'FatalExecutionEngineError' : 'The runtime
has encountered a fatal error. The address of the error was at
0x6f25e01e, on thread 0x431c. The error code is 0xc0000005. This error
may be a bug in the CLR or in the unsafe or non-verifiable portions of
user code. Common sources of this bug include user marshaling errors
for COM-interop or PInvoke, which may corrupt the stack.'
Just to ensure the validity of the void pointer I made, I call CostFn() at line 81 with the void * pointer passed as an argument to InitOptimizer() and everything works. But in line 85 when the same CostFn() is called with the optimParamsPtr pointing to data copied by memcpy_s, I get the error. So I am guessing something is going wrong with the memcpy_s step. Anyone have any ideas as to what?
#include "pch.h"
#include <iostream>
using namespace System;
using namespace System::Runtime::InteropServices;
using namespace std;
// An optimizer for various kinds of objects
class Optimizer // GSL requires this to be an unmanaged class
{
public:
double InitOptimizer(int ptrID, void *optimParams, size_t optimParamsSize);
void FreeOptimizer();
void * optimParamsPtr;
private:
double cost = 0;
};
ref class Foo // A class whose objects can be optimized
{
private:
int a; // An internal variable that can be changed to optimize the object
Optimizer *fooOptimizer; // Optimizer for a Foo object
public:
Foo(int val) // Constructor
{
a = val;
fooOptimizer = new Optimizer;
}
~Foo()
{
if (fooOptimizer != NULL)
{
delete fooOptimizer;
}
}
void SetA(int val) // Mutator
{
a = val;
}
int GetA() // Accessor
{
return a;
}
double Optimize(int ptrID); // Optimize object
// ptrID is a variable just to change behavior of Optimize() and show what works and what doesn't
};
ref struct Params // Parameters required by the cost function
{
int cost_scaling;
Foo ^ FooObj;
};
double CostFn(void *params) // GSL requires cost function to be of this type and cannot be a member of a class
{
// Cast void * to Params type
GCHandle h = GCHandle::FromIntPtr(IntPtr(params));
Params ^ paramsArg = safe_cast<Params^>(h.Target);
h.Free(); // Deallocate
// Return the cost
int val = paramsArg->FooObj->GetA();
return (double)(paramsArg->cost_scaling * val);
}
double Optimizer::InitOptimizer(int ptrID, void *optimParamsArg, size_t optimParamsSizeArg)
{
optimParamsPtr = ::operator new(optimParamsSizeArg);
memcpy_s(optimParamsPtr, optimParamsSizeArg, optimParamsArg, optimParamsSizeArg);
double ret_val;
// Here is where the GSL stuff would be. But I replace that with a call to CostFn to show the error
if (ptrID == 1)
{
ret_val = CostFn(optimParamsArg); // Works
}
else
{
ret_val = CostFn(optimParamsPtr); // Doesn't work
}
return ret_val;
}
// Release memory used by unmanaged variables in Optimizer
void Optimizer::FreeOptimizer()
{
if (optimParamsPtr != NULL)
{
delete optimParamsPtr;
}
}
double Foo::Optimize(int ptrID)
{
// Create and initialize params object
Params^ paramsArg = gcnew Params;
paramsArg->cost_scaling = 11;
paramsArg->FooObj = this;
// Convert Params type object to void *
void * paramsArgVPtr = GCHandle::ToIntPtr(GCHandle::Alloc(paramsArg)).ToPointer();
size_t paramsArgSize = sizeof(paramsArg); // size of memory block in bytes pointed to by void pointer
double result = 0;
// Initialize optimizer
result = fooOptimizer->InitOptimizer(ptrID, paramsArgVPtr, paramsArgSize);
// Here is where the loop that does the optimization will be. Removed from this example for simplicity.
return result;
}
int main()
{
Foo Foo1(2);
std::cout << Foo1.Optimize(1) << endl; // Use orig void * arg in line 81 and it works
std::cout << Foo1.Optimize(2) << endl; // Use memcpy_s-ed new void * public member of Optimizer in line 85 and it doesn't work
}
Just to reiterate I need to copy the params to a member in the optimizer because the optimizer will run all through the lifetime of the Foo object. So it needs to exist as long as the Optimizer object exist and not just in the scope of Foo::Optimize()
/clr support need to be selected in project properties for the code to compile. Running on an x64 solution platform.
Update 1: While trying to debug this, I got suspicious of the way I get the size of paramsArg at line 109. Looks like I am getting the size of paramsArg as size of int cost_scaling plus size of the memory storing the address to FooObj instead of the size of memory storing FooObj itself. I realized this after stumbling across this answer to another post. I confirmed this by checking the value of paramsArg after adding some new dummy double members to Foo class. As expected the value of paramsArg doesn't change. I suppose this explains why I get the error. A solution would be to write code to correctly calculate the size of a Foo class object and set that to paramsArg instead of using sizeof. But that is turning out to be too complicated and probably another question in itself. For example, how to get size of a ref class object? Anyways hopefully someone will find this helpful.

Parameters to use in a Vapi definition for passing arrays by reference

I have the following C code that uses libmodbus to read a single device register using ModbusTCP:
modbus_t *ctx;
uint16_t tab_reg[16];
ctx = modbus_new_tcp("10.0.1.77", 502);
modbus_read_registers(ctx, 0x20, 2, tab_reg);
printf("reg = %d (0x%X)\n", tab_reg[0], tab_reg[0]);
printf("reg = %d (0x%X)\n", tab_reg[1], tab_reg[1]);
now trying to switch this over to Vala using a Vapi that I've generated, the contents of that for new and read are:
[CCode (cheader_filename = "modbus.h", cname = "modbus_new_tcp")]
public static unowned Modbus.modbus_t create_tcp (string ip_address, int port);
public static int read_registers (Modbus.modbus_t ctx, int addr, int nb, uint16 dest);
[CCode (cheader_filename = "modbus.h")]
and the translated Vala program is:
class ModbusReadTest : GLib.Object {
unowned Modbus.modbus_t ctx;
public void run () {
uint16 reg = 0x00;
ctx = create_tcp ("10.0.1.77", 502);
Modbus.read_registers (ctx, 0x20, 2, reg);
message ("reg = %d (0x%X)", reg, reg);
Modbus.close(ctx);
}
}
Coincidentally, when I compile this into C code and then into a binary using gcc I get the error:
read-registers-test.c:71:2: warning: passing argument 4 of ‘modbus_read_registers’ makes pointer from integer without a cast [enabled by default]
which is not surprising. But I'm not sure how I should go about modifying the Vapi contents to closer match the prototype in the libmodbus header:
int modbus_read_registers(modbus_t *ctx, int addr, int nb, uint16_t *dest);
I've tried a mix of array options and using 'out', but haven't been able to get more than a single double byte register at a time.
read_registers should probably be an instance method (on Modbus.modbus_t) instead of a static method, and Modbus.modbus_t should probably be renamed to something like Modbus.Context, create_tcp should probably be a constructor, and Modbus.close should be a free function on the Modbus.Context compact class, but that's beside the point of this question (if you stop by #vala on irc.gnome.org you can get help with that stuff).
You probably want to make it an array:
public static int read_registers (Modbus.modbus_t ctx, int addr, [CCode (array_length_pos = 2.5)] uint16[] dest);
Then you would do something like this in Vala:
public void run () {
uint16 reg[2];
ctx = create_tcp ("10.0.1.77", 502);
Modbus.read_registers (ctx, 0x20, reg);
message ("reg = %d (0x%X)", reg, reg);
Modbus.close(ctx);
}
For a port more faithful to the original C (where tab_reg has 16 elements instead of 2), you could use array slicing:
public void run () {
uint16 reg[16];
ctx = create_tcp ("10.0.1.77", 502);
Modbus.read_registers (ctx, 0x20, reg[0:2]);
stdout.printf ("reg = %d (0x%X)\n", reg, reg);
Modbus.close(ctx);
}
Note that if you make it an instance method you'll need to change the array_length_pos to 1.5.

what is wrong with following pthread program?

I am not able to execute pthreads program in c. Please tell me what is wrong with the following program. I am neither getting any error nor expected output.
void *worker(void * arg)
{
int i;
int *id=(int *)arg;
printf("Thread %d starts\n", *id );
}
void main(int argc, char **argv)
{
int thrd_no,i,*thrd_id,rank=0;
void *exit_status;
pthread_t *threads;
thrd_no=atoi(argv[1]-1);
thrd_id= malloc(sizeof(int)*(thrd_no));
threads=malloc(sizeof(pthread_t)*(thrd_no));
for(i=0;i<thrd_no;i++)
{
rank=i+1;
thrd_id[i]=pthread_create(&threads[i], NULL, worker, &rank);
}
for(i=0;i<thrd_no;i++)
{
pthread_join(threads[i], &exit_status);
}
}
thrd_no = atoi(argv[1] - 1); likely doesn't do what you intended; the way argv is normally passed into a new process and parsed into a C array, argv[1] - 1 is probably pointing at \0 (specifically, the \0 at the end of argv[0]). (More generally, indexing backwards off the start of a string is rarely correct.) The result is that atoi() will return 0 and no threads will be created. What did you actually intend to do there?
You are passing the same address &rank to each thread, so id and *id is the same for all your worker-s.
You should better allocate on the heap the address you pass to each worker routine.
You might also include <stdint.h and use intptr_t, e.g.
void worker (void* p)
{
intptr_t rk = (intptr_t) p;
/// etc
}
and call
intptr_t rank = i + 1;
thrd_id[i]=pthread_create(&threads[i], NULL, worker, (void*)rank);
You should learn to use a debugger and compile with all warnings and debug information, i.e. gcc -Wall -g (and improve your code till it gets no warnings, then use gdb)
code segment rank=i+1;
thrd_id[i]=pthread_create(&threads[i], NULL, worker, &rank);
will produce race condition.

Resources