summaryrefslogtreecommitdiff
path: root/test/log/log_process.cpp
blob: d46d5e354727bbc81d7906dadb9e1ef668f8e192 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
 * Copyright (C) 2019, Google Inc.
 *
 * log_process.cpp - Logging in isolated child process test
 */

#include <fcntl.h>
#include <iostream>
#include <random>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <vector>

#include <libcamera/event_dispatcher.h>
#include <libcamera/logging.h>
#include <libcamera/timer.h>

#include "libcamera/internal/log.h"
#include "libcamera/internal/process.h"
#include "libcamera/internal/thread.h"
#include "libcamera/internal/utils.h"

#include "test.h"

using namespace std;
using namespace libcamera;

static const string message("hello from the child");

LOG_DEFINE_CATEGORY(LogProcessTest)

class LogProcessTestChild
{
public:
	int run(int status, int num)
	{
		usleep(50000);

		string logPath = "/tmp/libcamera.worker.test." +
				 to_string(num) + ".log";
		if (logSetFile(logPath.c_str()) < 0)
			return TestSkip;

		LOG(LogProcessTest, Warning) << message;

		return status;
	}
};

class LogProcessTest : public Test
{
protected:
	int init()
	{
		random_device random;
		num_ = random();
		logPath_ = "/tmp/libcamera.worker.test." +
			   to_string(num_) + ".log";

		proc_.finished.connect(this, &LogProcessTest::procFinished);
		return 0;
	}

	int run()
	{
		EventDispatcher *dispatcher = Thread::current()->eventDispatcher();
		Timer timeout;

		int exitCode = 42;
		vector<std::string> args;
		args.push_back(to_string(exitCode));
		args.push_back(to_string(num_));
		int ret = proc_.start("/proc/self/exe", args);
		if (ret) {
			cerr << "failed to start process" << endl;
			return TestFail;
		}

		timeout.start(200);
		while (timeout.isRunning())
			dispatcher->processEvents();

		if (exitStatus_ != Process::NormalExit) {
			cerr << "process did not exit normally" << endl;
			return TestFail;
		}

		if (exitCode_ == TestSkip)
			return TestSkip;

		if (exitCode_ != exitCode) {
			cerr << "exit code should be " << exitCode
			     << ", actual is " << exitCode_ << endl;
			return TestFail;
		}

		int fd = open(logPath_.c_str(), O_RDONLY, S_IRUSR);
		if (fd < 0) {
			cerr << "failed to open tmp log file" << endl;
			return TestFail;
		}

		char buf[200];
		memset(buf, 0, sizeof(buf));
		if (read(fd, buf, sizeof(buf)) < 0) {
			cerr << "Failed to read tmp log file" << endl;
			close(fd);
			return TestFail;
		}
		close(fd);

		string str(buf);
		if (str.find(message) == string::npos)
			return TestFail;

		return TestPass;
	}

	void cleanup()
	{
		unlink(logPath_.c_str());
	}

private:
	void procFinished(Process *proc, enum Process::ExitStatus exitStatus, int exitCode)
	{
		exitStatus_ = exitStatus;
		exitCode_ = exitCode;
	}

	Process proc_;
	Process::ExitStatus exitStatus_;
	string logPath_;
	int exitCode_;
	int num_;
};

/*
 * Can't use TEST_REGISTER() as single binary needs to act as both
 * parent and child processes.
 */
int main(int argc, char **argv)
{
	if (argc == 3) {
		int status = std::stoi(argv[1]);
		int num = std::stoi(argv[2]);
		LogProcessTestChild child;
		return child.run(status, num);
	}

	return LogProcessTest().execute();
}
span>rounded_values) if sum_error == 0: return rounded_values # The next step is to distribute the error among the values, in a way that # will minimize the relative error introduced in individual values. We # extend the values list with the rounded value and original index for each # element, and sort by rounding error. Then we modify the elements with the # highest or lowest error, depending on whether the sum error is negative # or positive. values = [[value, round(value), index] for index, value in enumerate(values)] values.sort(key=lambda v: v[1] - v[0]) # It could also be argued that the key for the sort order should not be the # absolute rouding error but the relative error, as the impact of identical # rounding errors will differ for coefficients with widely different values. # This is a topic for further research. # # values.sort(key=lambda v: (v[1] - v[0]) / abs(v[0])) if sum_error > 0: for i in range(sum_error): values[i][1] += 1 else: for i in range(-sum_error): values[len(values) - i - 1][1] -= 1 # Finally, sort back by index, make sure the total rounding error is now 0, # and return the rounded values. values.sort(key=lambda v: v[2]) values = [value[1] for value in values] assert(sum(values) == sum_values) return values def main(argv): # Parse command line arguments. parser = argparse.ArgumentParser( description='Generate color space conversion table coefficients with ' 'configurable fixed-point precision.' ) parser.add_argument('--invert', '-i', action='store_true', help='Invert the color space conversion (YUV -> RGB)') parser.add_argument('--precision', '-p', default='Q1.7', help='The output fixed point precision in Q notation (sign bit excluded)') parser.add_argument('--quantization', '-q', choices=['full', 'limited'], default='limited', help='Quantization range') parser.add_argument('encoding', choices=encodings.keys(), help='YCbCr encoding') args = parser.parse_args(argv[1:]) try: precision = Precision(args.precision) except Exception: print(f'Invalid precision `{args.precision}`') return 1 encoding = encodings[args.encoding] quantization = Quantization[args.quantization.upper()] # Scale and round the encoding coefficients based on the precision and # quantization range. luma = True scaled_coeffs = [] for line in encoding: line = [scale_coeff(coeff, quantization, luma) for coeff in line] scaled_coeffs.append(line) luma = False if args.invert: scaled_coeffs = np.linalg.inv(scaled_coeffs) rounded_coeffs = [] for line in scaled_coeffs: line = [coeff * (1 << precision.fractional) for coeff in line] # For the RGB to YUV conversion, use a rounding method that preserves # the rounded sum of each line to avoid biases and overflow, as the sum # of luma and chroma coefficients should be 1.0 and 0.0 respectively # (in full range). For the YUV to RGB conversion, there is no such # constraint, so use simple rounding. if args.invert: line = [round(coeff) for coeff in line] else: line = round_array(line) # Convert coefficients to the number of bits selected by the precision. # Negative values will be turned into positive integers using 2's # complement. line = [coeff & ((1 << precision.total) - 1) for coeff in line] rounded_coeffs.append(line) # Print the result as C code. nbits = 1 << (precision.total - 1).bit_length() nbytes = nbits // 4 print(f'static const u{nbits} {"yuv2rgb" if args.invert else "rgb2yuv"}_{args.encoding}_{quantization.name.lower()}_coeffs[] = {{') for line in rounded_coeffs: line = [f'0x{coeff:0{nbytes}x}' for coeff in line] print(f'\t{", ".join(line)},') print('};') return 0 if __name__ == '__main__': sys.exit(main(sys.argv))