Reputation: 354
Note: I am completely new to working with different cpu architectures and working with C
/ gcc
I am trying to create an application written in plain C
, which receives UDP messages from a server that are serialized as a protobuf
. The implementation I am using is protobuf-c
.
Note that I have to support linux/amd64
, linux/arm/v7
and linux/arm64/v8
. The application will run on k8s, which is why I want to create Docker Images for these architectures. I am using ubuntu:20.04
as base image, which supports all these archs.
Here is my proto-file:
syntax = "proto3";
message power_measurement {
uint64 timestamp = 1;
double energy_consumption = 2;
float current = 3;
}
Here is my Dockerfile. Basically, I am copying the source files and the protobuf files that i generated with protoc
into the container, install the libs needed for compilation, compile my program and then extract the generated binary into the next stage.
# [Stage 1]: Compile program within Container
FROM ubuntu:20.04 AS builder
WORKDIR /builder
# Install dependencies needed for compiling
RUN apt-get update && \
apt-get install -y make build-essential libconfuse-dev libprotobuf-c-dev && \
ldconfig
# Copy program files and protobuffers
COPY * ./app/
COPY ./docker/protobuffers ./protobuffers
RUN cd ./app && make build
# [Stage 2]: Create Docker Image from compiled file in Stage 1
FROM ubuntu:20.04
WORKDIR /opt/powermeasurement-udp-client
RUN apt-get update && \
apt-get install -y libconfuse-dev libprotobuf-c-dev && \
ldconfig
RUN useradd --no-log-init --create-home --shell /bin/bash udp-client
COPY --from=builder --chown=udp-client:udp-client /builder/app/udp_client .
USER udp-client
# Force line-buffered output in order to see docker logs immediately.
# (Otherwise fully-buffered would be used by default as there is no tty)
CMD stdbuf -oL ./udp_client
make build
will execute:
gcc -Wall -g -I/usr/include udp_client.c path/to/my/protobuffers/powermeasurements/powermeasurement.pb-c.c -o udp_client -L/usr/lib -lprotobuf-c -lpthread -lconfuse
This works well for linux/amd64
and linux/arm64/v8
- the program runs and the messages I am receiving are correct. For linux/arm/v7
the program runs, but the messages are completely wrong. The fields seem to be in the wrong order, and one field is apparently emitted. Here is how it looks on AMD64 and ARMv8:
Received 14 bytes from 10.244.2.30
Timestamp: 0
Energy Consumption: 110487058.316032 mAs
Current: 155.692383 mA
Received 14 bytes from 10.244.2.30
Timestamp: 0
Energy Consumption: 110487471.646268 mAs
Current: 917.799988 mA
and here is the mess I am receiving on ARMv7:
Received 14 bytes from 172.17.0.4
Timestamp: 1073749324
Energy Consumption: 0.000000 mAs
Current: 788.196977 mA
Received 14 bytes from 172.17.0.4
Timestamp: 1073749324
Energy Consumption: 0.000000 mAs
Current: 962.835978 mA
Note that on ARMv7 there is a value other than 0 for the timestamp
, which is false, because these are test messages that always have a value of 0 for that field. Also, the energy consumption
is constantly 0, which is false - it should be a double that is constantly increasing with every message. Instead, the current
field is constantly increasing, although it is sent as a number between 0 and 1000, which is randomly generated.
Here is the code I use for serializing and deserializing:
void *tx_buffer;
unsigned tx_data_len;
while(1) {
PowerMeasurement mock_measurement = create_mock_measurement(previous_measurement_for_client);
tx_data_len = power_measurement__get_packed_size(&mock_measurement);
tx_buffer = malloc(tx_data_len);
power_measurement__pack(&mock_measurement, tx_buffer);
int err = sendto(server_socket, tx_buffer, tx_data_len, 0, (struct sockaddr *)&client_sockaddr, client_socklen);
if (err < 0) {
printf("Error occured during sending on server socket: %s\n", strerror(errno));
exit(EXIT_FAILURE);
}
free(tx_buffer);
sleep(SEND_INTERVAL_SECONDS);
}
uint8_t rx_buffer[1024];
size_t rx_data_len;
while (listen_for_data) {
rx_data_len = read(client_socket, rx_buffer, RECEIVE_BUFFER_SIZE);
if (rx_data_len < 0) {
printf("Error during receiving: %s\n", strerror(errno));
break;
}
printf("Received %ld bytes from %s", rx_data_len, server_ip);
PowerMeasurement *measurement = power_measurement__unpack(NULL, rx_data_len, rx_buffer);
if (measurement == NULL) {
printf("Failed to deserialize the received data!\n");
exit(EXIT_FAILURE);
}
latest_measurement = *measurement;
power_measurement__free_unpacked(measurement, NULL);
printf("\nTimestamp: %lu \nEnergy Consumption: %lf mAs \nCurrent: %f mA\n\n",
latest_measurement.timestamp, latest_measurement.energy_consumption, latest_measurement.current);
}
I really have tried everything at this point and couldn't find the error here. I even used some cross compiler arm-linux-gnueabihf-gcc
instead of plain gcc
and it still didnt work. The file
command says the generated binary is for ARM 32bit, so there should be nothing wrong.
Any help would be greatly appreciated.
Here are the bytes sent by the server (AMD64
in this case):
Sending 14 bytes to client
11:22:DF:2B:94:90:EF:77:40:1D:23:8A:60:43
Sending 14 bytes to client
11:22:B6:68:56:10:9B:87:40:1D:24:7E:06:44
Sending 14 bytes to client
11:56:88:D8:95:2A:E4:99:40:1D:D9:1F:E7:43
Here are the bytes that were received by the client on ARMv7
:+
Received 14 bytes from 172.17.0.3
11:22:DF:2B:94:90:EF:77:40:1D:23:8A:60:43
Timestamp: 1073749496
Energy Consumption: 0.000000 mAs
Current: 382.972798 mA
Received 14 bytes from 172.17.0.3
11:22:B6:68:56:10:9B:87:40:1D:24:7E:06:44
Timestamp: 1073749496
Energy Consumption: 0.000000 mAs
Current: 755.382977 mA
Received 14 bytes from 172.17.0.3
11:56:88:D8:95:2A:E4:99:40:1D:D9:1F:E7:43
Timestamp: 1073749496
Energy Consumption: 0.000000 mAs
Current: 1657.041587 mA
The power measurement message is created as follows:
static PowerMeasurement create_mock_measurement(PowerMeasurement prev_mock_measurement) {
double mock_consumption = (double)rand() / (double)(RAND_MAX / 1000.0);
float mock_current = (float)rand() / (float)(RAND_MAX / 1000.0);
PowerMeasurement mock_measurement = POWER_MEASUREMENT__INIT;
mock_measurement.energy_consumption = prev_mock_measurement.energy_consumption + mock_consumption;
mock_measurement.current = mock_current;
return mock_measurement;
}
Upvotes: 1
Views: 155
Reputation: 354
Answering my own question even though it is quite embarassing LOL
Changing
printf("\nTimestamp: %lu [...]
to
printf("\nTimestamp: %llu [...]
fixed it... Being a C newbie ain't easy.
I will never neglect compiler warnings anymore.
Upvotes: 3