sending a struct array with sun rpc from server to client - c

how can i correctly send a struct from the server to the client in ansi-c sun-rpc?
in my test.x IDL file i defined a struct cluster with a string and an int
and a type clusters which is a variable-length array of cluster elements:
struct cluster {
string name<255>;
int debuglevel;
};
typedef cluster clusters<32>;
i then changed the stubs generated by rpcgen like
test_server.c
clusters *
test_1_svc(void *argp, struct svc_req *rqstp)
{
static clusters result;
cluster cl1, cl2;
cl1.name="cl1";
cl1.debuglevel="1";
cl2.name="cl2";
cl2.debuglevel="2";
cluster clist[2];
clist[0]=cl1;
clist[1]=cl2;
result.clusters_len = 2;
result.clusters_val = &clist;
/*
* insert server code here
*/
return(&result);
}
and test_client.c
test_prog_1( char* host )
{
CLIENT *clnt;
clusters *result_1;
char* test_1_arg;
clnt = clnt_create(host, test_PROG, test_VERS, "udp");
if (clnt == NULL) {
clnt_pcreateerror(host);
exit(1);
}
result_1 = test_1((void*)&test_1_arg, clnt);
if (result_1 == NULL) {
clusters* rec_cls = malloc(2*sizeof(struct cluster));
if(xdr_clusters(&result_1, rec_cls)){
printf("got xdr_clusters");
}
clnt_perror(clnt, "call failed:");
}
clnt_destroy( clnt );
}
Both compile, but the server often segfaults after one or two request runs by the client and on the clientside the xdr_clusters function never returns true. It seems like some kind of memory mismanagement and I'm also not sure if I'm handling the serialization on the server-side correctly.
I just filled result.clusters_len and result.clusters_val with the appropiate values like they are defined in test.h (by rpcgen):
typedef struct {
u_int clusters_len;
cluster *clusters_val;
} clusters;
Do I have to make use of xdr_clusters on the server side for this to correctly serialize the result?
thank you

okay, i figured my mistakes, lets summarize them:
know how to initialize an int correctly (without the quotes of course...)
forget about that clist nonsense, just malloc the internal pointer of the result struct directly
read the damn compiler warnings: when it tells you, that there are functions declared implicitly and you didn't want implicit declarations, then there is possibly something missing, in my case i needed to include stdlib.h and stdio.h to get malloc, printf and exit functions for server and client stubs.
on the clientside: why should we do anything except throwing an error if the result is NULL? see the new client code below to check correct result printing
test_server.c
test_1_svc(void *argp, struct svc_req *rqstp){
static clusters result;
cluster cl1, cl2;
cl1.name="cl1";
cl1.debuglevel=1;
cl2.name="cl2";
cl2.debuglevel=2;
result.clusters_len = 2;
result.clusters_val = malloc(2*sizeof(struct cluster));
result.clusters_val[0]=cl1;
result.clusters_val[1]=cl2;
return(&result);
}
test_client.c
test_prog_1( char* host )
{
CLIENT *clnt;
clusters *result_1;
char* test_1_arg;
clnt = clnt_create(host, test_PROG, test_VERS, "udp");
if (clnt == NULL) {
clnt_pcreateerror(host);
exit(1);
}
result_1 = test_1((void*)&test_1_arg, clnt);
if (result_1 == NULL) {
clnt_perror(clnt, "call failed:");
}else{
printf("I got %d cluster structs in an array\n",result_1->clusters_len);
int j;
for(j=0;j<result_1->clusters_len;j++){
printf("cluster #%d: %s#runlevel %d\n",j,result_1->clusters_val[j].name,result_1->clusters_val[j].debuglevel);
}
}
clnt_destroy( clnt );
}
as a result, we get some nice values on the clientside printed
and of course no segfaults anymore on the serverside:
lars$ ./test_client localhost
I got 2 cluster structs in an array
cluster #0: cl1#runlevel 1
cluster #1: cl2#runlevel 2

Related

How to properly initialize a string that is a field of an autogenerated structure from RPC in ANSI-C

I'm programming a RPC client-server application using ANSI-C, in Ubuntu 17.04. I'm having big troubles at the time of properly initializing strings that are auto-generated from a predefined structures using RPC, and passing it from client-side to server.
Here's my definition of the structure (in RPC):
struct usuario{
int codigo;
string nombreUsuario<25>;
string nombreReal<25>;
string apellido1<25>;
string apellido2<25>;
string clave<25>;
int num_tarjeta;
string fechaAlta<10>;
string fechaBaja<10>;
};
Here the remote function definition (in RPC):
program NPROG {
version NVERS {
string registrarse(usuario)=2;
}=1;
}=0x20000001;
NOTE: I initialize all the fields, and all the strings before the remote call, the strings like the way I'm going to show below, but I'm writing just one here in order to not writing a long question.
Now I've got something like this in my client side:
void
nprog_1(char *host)
{
CLIENT *clnt;
char * *result_1;
usuario register_arg;
#ifndef DEBUG
clnt = clnt_create (host, NPROG, NVERS, "tcp");
if (clnt == NULL) {
clnt_pcreateerror (host);
exit (1);
}
#endif /* DEBUG */
register_arg.codigo = 0;
// In this way, Segment fault, the two functions, strcpy and strncpy
// strcpy(register_arg.nombreUsuario, "SomeName");
strncpy(register_arg.nombreUsuario, "SomeName", sizeof(register_arg.nombreUsuario));
// call failed en registro: RPC: Remote system error
// register_arg.nombreUsuario = "SomeName";
result_1 = registrarse_1(&register_arg, clnt);
if (result_1 == (char **) NULL) {
clnt_perror (clnt, "call failed en registro");
}
#ifndef DEBUG
clnt_destroy (clnt);
#endif /* DEBUG */
}
I don't put the remote/server code because I'm not even able to get there.
The problem is, that in the generated struct for strings no memory is preallocated, these are just dangling pointers! So you have to care about the memory for the string yourself, you could for example pass a constant:
register_arg.nombreUsuario = "SomeName";
or allocate the memory yourself:
register_arg.nombreUsuario = strdup("SomeName");
Then it should work as you expect.

IIO device buffer always null

I am using an IMU sensor called LSM6DSL with the iio drivers. They work fine if I display the raw values with the command:
cat /sys/bus/iio/devices/iio:device0/in_accel_x_raw
Then I decided to use the libiio so I can read all these values from a C program :
struct iio_context *context = iio_create_local_context();
struct iio_device *device = iio_context_get_device(context, 1);
struct iio_channel *chan = iio_device_get_channel(device, 0);
iio_channel_enable(chan);
if (iio_channel_is_scan_element(chan) == true)
printf("OK\n");
struct iio_channel *chan2 = iio_device_get_channel(device, 1);
iio_channel_enable(chan2);
struct iio_buffer *buff = iio_device_create_buffer(device, 1, true);
if (buff == NULL)
{
printf("Error: %s\n", strerror(errno));
return (1);
}
And this is the result :
OK
Error: Device or resource busy
Am I missing something? Let me know if you need more informations.
I guess I found the answer, and I didn't pay attention to the effects of the ncurses library (sorry for not mentioning that I was using it).
I moved these functions before the initialization of ncurses and now the buffer is created successful.

Problems with netdev_alloc and netdev_priv in kernel network driver

I've got a custom piece of FPGA logic which I've implemented a functioning char driver for, and I'm trying to get it to work as a network driver as well now. I'm following along as best I can using the LDD book, snull code, and loopback.c, dummy.c, and other drivers from the kernel as examples.
The code I've got below (obviously ripped out of the larger file) is the networking driver code I have so far; I can successfully insmod, and "ifconfig optical0" shows that my MTU & flags are correct so I know at least it's being registered.
Now I'm trying to implement the struct net_device's private field (void* priv) to store stats and other things in, and it's resulting in segfaults. Here's the code so far:
struct serdes_device {
struct serdes_regs __iomem *regs;
struct serdes_dma *tx_dma,
*rx_dma;
struct device *dev;
struct net_device *netdev;
struct resource serdes_res;
struct cdev cdev;
int major, minor;
int tx_kbps;
int rx_kbps;
};
struct optical_priv {
struct serdes_device *serdes_dev;
struct net_device_stats stats;
};
static const struct net_device_ops optical_netdev_ops = {
.ndo_get_stats = optical_stats,
.ndo_start_xmit = optical_xmit,
/* and so on, not including the ops functions for brevity */
};
void optical_setup(struct net_device *dev)
{
dev->netdev_ops = &optical_netdev_ops;
dev->destructor = free_netdev;
dev->tx_queue_len = 1;
dev->type = ARPHRD_NONE;
dev->hard_header_len = SERDES_FRAME_HEADER_LENGTH;
dev->mtu = SERDES_FRAME_TOTAL_LENGTH;
dev->addr_len = 0;
dev->flags &= ~IFF_BROADCAST;
/* more flags & features cut */
}
static int optical_init(struct net_device *netdev)
{
int err;
struct optical_priv *priv;
netdev = alloc_netdev(sizeof(struct optical_priv), "optical%d", optical_setup);
if (!netdev)
return -ENOMEM;
err = register_netdev(netdev);
if (err < 0)
goto err;
priv = netdev_priv(netdev);
printk(KERN_WARNING "priv is at address 0x%p\n", priv);
return 0;
err:
free_netdev(netdev);
return err;
}
static int serdes_of_probe(struct platform_device *op)
{
struct serdes_device *serdes_dev;
struct optical_priv *priv;
int err = 0;
serdes_dev = kzalloc(sizeof(struct serdes_device), GFP_KERNEL);
/* A bunch of unrelated openfirmware & cdev code removed. */
err = optical_init(serdes_dev->netdev);
if (err < 0)
dev_err(serdes_dev->dev, "Error %d initing optical link\n", err);
priv = netdev_priv(serdes_dev->netdev);
if (!priv)
dev_err(serdes_dev->dev, "priv is null... \n");
dev_info(serdes_dev->dev, "priv is at 0x%p\n", priv);
dev_info(&op->dev, "Done probing.\n");
return 0;
out_free_serdes:
kfree(serdes_dev);
out_return:
return err;
}
So as I understand it, I'm allocating space for and initializing my serdes device (which I know works as a char driver). Then I call optical_init which allocs, registers, and configures serdes_dev->netdev. alloc_netdev is supposed to allocate room for the (void *)priv field as well which is why sizeof(struct optical_priv) is passed in.
The output after insmod of the two print statements in there look like this:
priv is at address 0xdd2de500
priv is at 0x00000500
Done probing.
and obviously trying to access priv in any way after that causes a segfault. I think my confusion is about why netdev_priv() returns the two different values when called from the two different functions-- shouldn't it be allocated and correct anytime after alloc_netdev?
e: I think I'm also confusing myself by looking for examples in too many drivers, from too many eras, steeped in the history of hardware and kernel APIs past... if anyone has a good basic driver recommendation for me to start with, I'm more than happy reading code to learn.
I think that the problem is here:
err = optical_init(serdes_dev->netdev);
You are passing an invalid pointer to a netdev structure. Then in optical_init() you change the local value of the pointer with:
netdev = alloc_netdev(sizeof(struct optical_priv), "optical%d", optical_setup);
Now you have a valid pointer to a struct net_device. But this pointer value does not apply to the serdes_dev->netdev variable but only locally in optical_init(). In consequence of this, also the pointer to priv is invalid. Here a little example that show the issue:
#include <stdio.h>
#include <stdlib.h>
struct test {
int first;
int *second;
};
void allocate_memory(int *ptr)
{
ptr = malloc(10);
printf("while allocating %p\n", ptr);
}
int main(void) {
struct test *p1;
p1= malloc(sizeof(struct test));
printf("before %p\n", p1->second);
allocate_memory(p1->second);
printf("after %p\n", p1->second);
free(p1);
free(p2);
return EXIT_SUCCESS;
}
The output is:
before (nil)
while allocating 0x23ee030
after (nil)

Design Pattern in C - Reading from multiple devices and interfaces

I'm need to implement a few functions that read messages from different devices that have different interface possibilities and different message structure. (but the messages have pretty much the same data)
Eg
Device_A {
message type: A
iface 1: tcp
}
Device_B {
message type: B
iface 1: serial
iface 2: tcp
}
... and so on
In my main...
struct msg_data;
while(user_wants_to_read) {
read_msg(); // reads and sets data in msg_data
do_work(msg_data);
}
In an OO Language I would use the strategy pattern. I think I could do this with a void* read_func;?
I'm inexperienced in C and I want to learn to program this like a good C programmer would do. What sort of design pattern/functions should I implement?
It sounds like you got two or more different abstractions to solve for:
Different stream sources (TCP vs. Serial). Is the the TCP protocol the same for device A and device B?
Different message types that are structurally different but semantically the same.
Different device classes (device A vs Device B)
I would focus on a strategy pattern with factories for reading from a stream. And then perhaps an adapter or strategy pattern for getting more data into message objects. But I wouldn't get held up on "which design pattern". More likely, just think in terms of interfaces.
So to start, perhaps abstracting out the serial and TCP streaming into different implementations with the same interface. One implementation that knows how connect and read bytes from a TCP socket without regard to the message contents. Another that knows how to read from a serial port. They should have the same "interface". Here's a lightweight example of a a "byte stream interface" with some hacked up socket code thrown. Forgive me if this doesn't compile. I might have a typo valid in C++ by wrong in C. In any case, it's just an example demonstrating interfaces through function table pointers.
My thinking on suggesting this is, "how would I implement this in C++?" And then I'm transposing my answer to pure "C". (Note: I'm likely making some declaration mistakes below.)
struct ByteStreamer;
typedef int (*ReadFunc)(ByteStreamer*, char* buffer, int count);
typedef int (*OpenFunc)(ByteStreamer*, char* url); // maybe 'open' isn't needed if it's handled by the factory
typedef int (*CloseFunc)(ByteStreamer*);
typedef void (*DisposeFunc)(ByteStreamer*);
typedef struct _ByteStreamer
{
ReadFunc readfunc;
OpenFunc openfunc;
CloseFunc closefunc;
DisposeFunc dispose;
// private data meant for the "class"
void* instancedata;
} ByteStreamer;
struct _tcpconnection
{
int socket;
sockaddr_in addrRemote;
} TCPConnection;
struct _serialconnection
{
int filehandle;
int baud;
} SerialConnection;
// ---------------------------------------
ByteStream* CreateStreamForTCP(const sockaddr_in *pAddr) // pass additional parameter as needed
{
ByteStreamer* pStream = (ByteStreamre*)malloc(sizeof(ByteStreamer));
TCPConnection* pTCPConnection = (TCPConnection*)malloc(sizeof(TCPConnection*));
pTCPConnection->socket = -1;
pTCPConnection->addrRemote = *pAddr;
pStream->instancedata = pTCPConnection;
pStream->ReadFunc = TCPRead;
pStream->OpenFunc = TCPOpen;
pStream->CloseFunc = TCPClose;
pStream->DisposeFunc = TCPDispose;
pStream->type = STREAM_TYPE_TCP;
return pStream;
}
int TCPRead(ByteStream* pStream, char* buffer, int count)
{
return recv(((TCPConnection*)pStream->instancedata)->socket, buffer, count, 0);
}
int TCPOpen(ByteStream* pStream, char* url)
{
// it's up to you if you want to encapsulate the socket address in url or in the instance data
TCPConnection* pConn = (TCPConnection*)(pStream->instancedata);
int sock = socket(AF_INET, SOCK_STREAM, 0);
connect(&pConn->addrRemote, sizeof(pConn->addrRemote));
return (pConn->sock >= 0); // true/false return;
}
void TCPClose(ByteStream* pStream)
{
TCPConnection* pConn = (TCPConnection*)(pStream->instancedata);
close(pConn->sock);
}
void TCPDispose(ByteStream* pStream)
{
free(pStream->instancedata);
free(pStream);
}
Now replace all the TCP code above with an equivalent serial port implementation. It would also be a good idea to implement a "file stream" (or "in memory stream") version of the ByteStream struct. Because it will be very useful in unit tests for higher level code.
So after you get all the byte stream implementations worked out, then move onto parsing device specific messages.
typedef struct _Message_A
{
// A specific data fields
} Message_A;
struct _Message_B
{
// B specific data fields
} Message_B;
struct Message
{
// commonality between Message_A and Message_B
};
typedef (*ReadMessageFromStream)(MessageReader* pReader, Message* pMsg); // pStream is an in-param, pMSg is an out-param.
typedef (*MessageReaderDispose)();
struct MessageReader
{
ReadMessageFromStream reader;
MessageReaderDispose dispose;
// -----------------------------
ByteStream* pStream;
void *instancedata;
};
// function to read a "Message_A" from a stream - and then transpose it to the generic Message type
int ReadMessage_A(ByteStream* pStream, Message* pMsg);
// function to read a "Message_B" from a stream - and then transpose it to the generic Message type
int ReadMessage_B(ByteStream* pStream, Message* pMsg);
So what's really cool about implementing ReadMessage_A and ReadMessage_B is that you can pass that "file stream" implementation of ByteStream and make some really good unit tests. So when you plug in the TCP or serial version, it has a high chance of just working (assuming your TCP and serial code are tested seperately).
And then perhaps a factory method off each class for creating the uber ReadMessageFromStream:
MessageReader* CreateTCPReaderForDeviceA(DeviceA* pA, sockaddr_in* pAddr)
{
MessageReader *pMR = (vMessageReader*)malloc(sizeof(MessageReader));
pMR->pStream = CreateStreamForTCP(pAddr);
pMR->pStream->Open();
pMR->reader = ReadMessage_A;
return pMR;
}
MessageReader* CreateSerialReaderForDeviceB(DeviceB* pB, int comport)
{
MessageReader *pMR = (vMessageReader*)malloc(sizeof(MessageReader));
pMR->pStream = CreateStreamForSerial(comport);
pMR->pStream->Open();
pMR->reader = ReadMessage_B;
return pMR;
}
And then your main loop looks something like the following:
if ((type == DEVICE_A) && (source == TCP))
pReader = CreateTCPReaderForDeviceA(pDevice, &addr)
else if ((type == DEVICE_B) && (source == SERIAL))
pReader = CreateSerialReaderForDeviceB(pDeviceB, 1);
// read the message
Message msg;
pReader->reader(pReader, &msg);
pReader->Dispose(); // free all the data allocated and close connections/files
Wooh.... I'm tired of typing this point. hope this helps.
I would agree with #rsaxvc. Function pointers are probably the best way to go about this. A google search turned up this: Strategy pattern in C
And for your message struct, you could use nested struct to emulate OO class inheritance
struct base {
// common members
}
struct child1 {
struct base;
// other data members
}
or simplely:
struct child2 {
// same data members as base
// other data members
}
use a base* parameter

C structs strange behaviour

I have some long source code that involves a struct definition:
struct exec_env {
cl_program* cpPrograms;
cl_context cxGPUContext;
int cpProgramCount;
int cpKernelCount;
int nvidia_platform_index;
int num_cl_mem_buffs_used;
int total;
cl_platform_id cpPlatform;
cl_uint ciDeviceCount;
cl_int ciErrNum;
cl_command_queue commandQueue;
cl_kernel* cpKernels;
cl_device_id *cdDevices;
cl_mem* cmMem;
};
The strange thing, is that the output of my program is dependent on the order in which I declare the components of this struct. Why might this be?
EDIT:
Some more code:
int HandleClient(int sock) {
struct exec_env my_env;
int err, cl_err;
int rec_buff [sizeof(int)];
log("[LOG]: In HandleClient. \n");
my_env.total = 0;
//in anticipation of some cl_mem buffers, we pre-emtively init some. Later, we should have these
//grow/shrink dynamically.
my_env.num_cl_mem_buffs_used = 0;
if ((my_env.cmMem = (cl_mem*)malloc(MAX_CL_BUFFS * sizeof(cl_mem))) == NULL)
{
log("[ERROR]:Failed to allocate memory for cl_mem structures\n");
//let the client know
replyHeader(sock, MALLOC_FAIL, UNKNOWN, 0, 0);
return EXIT_FAILURE;
}
my_env.cpPlatform = NULL;
my_env.ciDeviceCount = 0;
my_env.cdDevices = NULL;
my_env.commandQueue = NULL;
my_env.cxGPUContext = NULL;
while(1){
log("[LOG]: Awaiting next packet header... \n");
//read the first 4 bytes of the header 1st, which signify the function id. We later switch on this value
//so we can read the rest of the header which is function dependent.
if((err = receiveAll(sock,(char*) &rec_buff, sizeof(int))) != EXIT_SUCCESS){
return err;
}
log("[LOG]: Got function id %d \n", rec_buff[0]);
log("[LOG]: Total Function count: %d \n", my_env.total);
my_env.total++;
//now we switch based on the function_id
switch (rec_buff[0]) {
case CREATE_BUFFER:;
{
//first define a client packet to hold the header
struct clCreateBuffer_client_packet my_client_packet_hdr;
int client_hdr_size_bytes = CLI_PKT_HDR_SIZE + CRE_BUFF_CLI_PKT_HDR_EXTRA_SIZE;
//buffer for the rest of the header (except the size_t)
int header_rec_buff [(client_hdr_size_bytes - sizeof(my_client_packet_hdr.buff_size))];
//size_t header_rec_buff_size_t [sizeof(my_client_packet_hdr.buff_size)];
size_t header_rec_buff_size_t [1];
//set the first field
my_client_packet_hdr.std_header.function_id = rec_buff[0];
//read the rest of the header
if((err = receiveAll(sock,(char*) &header_rec_buff, (client_hdr_size_bytes - sizeof(my_client_packet_hdr.std_header.function_id) - sizeof(my_client_packet_hdr.buff_size)))) != EXIT_SUCCESS){
//signal the client that something went wrong. Note we let the client know it was a socket read error at the server end.
err = replyHeader(sock, err, CREATE_BUFFER, 0, 0);
cleanUpAllOpenCL(&my_env);
return err;
}
//read the rest of the header (size_t)
if((err = receiveAll(sock, (char*)&header_rec_buff_size_t, sizeof(my_client_packet_hdr.buff_size))) != EXIT_SUCCESS){
//signal the client that something went wrong. Note we let the client know it was a socket read error at the server end.
err = replyHeader(sock, err, CREATE_BUFFER, 0, 0);
cleanUpAllOpenCL(&my_env);
return err;
}
log("[LOG]: Got the rest of the header, packet size is %d \n", header_rec_buff[0]);
log("[LOG]: Got the rest of the header, flags are %d \n", header_rec_buff[1]);
log("[LOG]: Buff size is %d \n", header_rec_buff_size_t[0]);
//set the remaining fields
my_client_packet_hdr.std_header.packet_size = header_rec_buff[0];
my_client_packet_hdr.flags = header_rec_buff[1];
my_client_packet_hdr.buff_size = header_rec_buff_size_t[0];
//get the payload (if one exists)
int payload_size = (my_client_packet_hdr.std_header.packet_size - client_hdr_size_bytes);
log("[LOG]: payload_size is %d \n", payload_size);
char* payload = NULL;
if(payload_size != 0){
if ((payload = malloc(my_client_packet_hdr.buff_size)) == NULL){
log("[ERROR]:Failed to allocate memory for payload!\n");
replyHeader(sock, MALLOC_FAIL, UNKNOWN, 0, 0);
cleanUpAllOpenCL(&my_env);
return EXIT_FAILURE;
}
if((err = receiveAllSizet(sock, payload, my_client_packet_hdr.buff_size)) != EXIT_SUCCESS){
//signal the client that something went wrong. Note we let the client know it was a socket read error at the server end.
err = replyHeader(sock, err, CREATE_BUFFER, 0, 0);
free(payload);
cleanUpAllOpenCL(&my_env);
return err;
}
}
//make the opencl call
log("[LOG]: ***num_cl_mem_buffs_used before***: %d \n", my_env.num_cl_mem_buffs_used);
cl_err = h_clCreateBuffer(&my_env, my_client_packet_hdr.flags, my_client_packet_hdr.buff_size, payload, &my_env.cmMem);
my_env.num_cl_mem_buffs_used = (my_env.num_cl_mem_buffs_used+1);
log("[LOG]: ***num_cl_mem_buffs_used after***: %d \n", my_env.num_cl_mem_buffs_used);
//send back the reply with the error code to the client
log("[LOG]: Sending back reply header \n");
if((err = replyHeader(sock, cl_err, CREATE_BUFFER, 0, (my_env.num_cl_mem_buffs_used -1))) != EXIT_SUCCESS){
//send the header failed, so we exit
log("[ERROR]: Failed to send reply header to client, %d \n", err);
log("[LOG]: OpenCL function result was %d \n", cl_err);
if(payload != NULL) free(payload);
cleanUpAllOpenCL(&my_env);
return err;
}
//now exit if failed
if(cl_err != CL_SUCCESS){
log("[ERROR]: Error executing OpenCL function clCreateBuffer %d \n", cl_err);
if(payload != NULL) free(payload);
cleanUpAllOpenCL(&my_env);
return EXIT_FAILURE;
}
}
break;
Now what's really interesting is the call to h_clCreateBuffer. This function is as follows
int h_clCreateBuffer(struct exec_env* my_env, int flags, size_t size, void* buff, cl_mem* all_mems){
/*
* TODO:
* Sort out the flags.
* How do we store cl_mem objects persistantly? In the my_env struct? Can we have a pointer int the my_env
* struct that points to a mallocd area of mem. Each malloc entry is a pointer to a cl_mem object. Then we
* can update the malloced area, growing it as we have more and more cl_mem objects.
*/
//check that we have enough pointers to cl_mem. TODO, dynamically expand if not
if(my_env->num_cl_mem_buffs_used == MAX_CL_BUFFS){
return CL_MEM_OUT_OF_RANGE;
}
int ciErrNum;
cl_mem_flags flag;
if(flags == CL_MEM_READ_WRITE_ONLY){
flag = CL_MEM_READ_WRITE;
}
if(flags == CL_MEM_READ_WRITE_OR_CL_MEM_COPY_HOST_PTR){
flag = CL_MEM_READ_WRITE | CL_MEM_COPY_HOST_PTR;
}
log("[LOG]: Got flags. Calling clCreateBuffer\n");
log("[LOG]: ***num_cl_mem_buffs_used before in function***: %d \n", my_env->num_cl_mem_buffs_used);
all_mems[my_env->num_cl_mem_buffs_used] = clCreateBuffer(my_env->cxGPUContext, flag , size, buff, &ciErrNum);
log("[LOG]: ***num_cl_mem_buffs_used after in function***: %d \n", my_env->num_cl_mem_buffs_used);
log("[LOG]: Finished clCreateBuffer with id: %d \n", my_env->num_cl_mem_buffs_used);
//log("[LOG]: Finished clCreateBuffer with id: %d \n", buff_counter);
return ciErrNum;
}
The first time round the while loop, my_env->num_cl_mem_buffs_used is increased by 1. However, the next time round the loop, after the call to clCreateBuffer, the value of my_env->num_cl_mem_buffs_used gets reset to 0. This does not happen when I change the order in which I declare the members of the struct! Thoughts? Note that I've omitted the other case statements, all of which so similar things, i.e. updating the structs members.
Well, if your program dumps the raw memory content of the object of your struct type, then the output will obviously depend on the ordering of the fields inside your struct. So, here's one obvious scenario that will create such a dependency. There are many others.
Why are you surprised that the output of your program depends on that order? In general, there's nothing strange in that dependency. If you base your verdict of on the knowledge of the rest of the code, then I understand. But people here have no such knowledge and we are not telepathic.
It's hard to tell. Maybe you can post some code. If I had to guess, I'd say you were casting some input file (made of bytes) into this struct. In that case, you must have the proper order declared (usually standardized by some protocol) for your struct in order to properly cast or else risk invalidating your data.
For example, if you have file that is made of two bytes and you are casting the file to a struct, you need to be sure that your struct has properly defined the order to ensure correct data.
struct example1
{
byte foo;
byte bar;
};
struct example2
{
byte bar;
byte foo;
};
//...
char buffer[];
//fill buffer with some bits
(example1)buffer;
(example2)buffer;
//those two casted structs will have different data because of the way they are defined.
In this case, "buffer" will always be filled in the same manner, as per some standard.
Of course the output depends on the order. Order of fields in a struct matters.
An additional explanation to the other answers posted here: the compiler may be adding padding between fields in the struct, especially if you are on a 64 bit platform.
If you are not using binary serialization, then your best bet is an invalid pointer issue. Like +1 error, or some invalid pointer arithmetics can cause this. But it is hard to know without code. And it is still hard to know, even with the code. You may try to use some kind of pointer validation/tracking system to be sure.
other guesses
by changing the order you are having different uninitialized values in the struct. A pointer being zero or not zero for example
you somehow manage to overrun an item (by casting ) and blast later items. Different items get blasted depending on order
This may happen if your code uses on "old style" initializer as from C89. For a simple example
struct toto {
unsigned a;
double b;
};
.
.
toto A = { 0, 1 };
If you interchange the fields in the definition this remains a valid initializer, but your fields are initialized completely different. Modern C, AKA C99, has designated initializers for that:
toto A = { .a = 0, .b = 1 };
Now, even when reordering your fields or inserting a new one, your initialization remains valid.
This is a common error which is perhaps at the origin of the initializerphobia that I observe in many C89 programs.
You have 14 fields in your struct, so there is 14! possible ways the compiler and/or standard C library can order them during the output.
If you think from the compiler designer's point of view, what should the order be? Random is certainly not useful. The only useful order is the order in which the struct fields were declared by you.

Resources