I need to emulate the workings of a piece of hardware (NOT for video games).
This component runs at 1 Ghz, while my PC's run at 2.5 and 2.7 Ghz.
So I am trying to tell the pc to run this particular process at a lower speed.
I have tried timers, but it won't do: when dealing with small intervals of time the process won't keep track of time with accuracy (I'd need to keep track of milliseconds and it can't be done neatly) Also to calculate time intervals you would loose some CPU time
Keep in mind that I am not outsourcing to the community, i am working on my own but maybe you guys could help brainstorming :)
From what I understand from your question & comments, you need to run a program at a 12.5 Hz
CPU. I can think of single stepping through the instructions, much like a debugger, but instead of waiting for you to single step the instructions, it executes each instruction at every time delay (much like what you said you tried). So, if this premise is wrong, please tell me and I'll delete my answer, cause it's based on it.
If your clock count is 80ms
, that means you can execute at least one instruction per 80ms
. Unfortunately the sleep function will only take unsigned int
arguments in seconds, so that's not gonna work. However, there's the syscall nanosleep, which lets you adjust a sleep in nanoseconds.
So, to convert that milliseconds to nano, you multiply it by 106, which will give you 80000000 nanoseconds
of sleep time. As you already mentioned, there will be some time waste from the calling of functions and the emulator time, but I think that's the price you gotta pay for an emulator(and you can always tinker with the times to make finer adjustments). So, the nanosleep
is:
#include <time.h>
int nanosleep(const struct timespec *req, struct timespec *rem);
struct timespec {
time_t tv_sec; /* seconds */
long tv_nsec; /* nanoseconds */
};
The other one is the Linux syscall ptrace
#include <sys/ptrace.h>
long ptrace(enum __ptrace_request request, pid_t pid,
void *addr, void *data);
This function will let you do all sorts of things with the traced process, and I suggest you read the manual. It's very informing. That syscall is the base function of debugging software, and you can read a tutorial on How Debuggers Work here.
In fact, my idea came from that tutorial (I read it a couple of days ago), and I'll modify that code a little bit to do the emulator, so I also suggest reading the tutorial.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
#include <sys/ptrace.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/reg.h>
#include <sys/user.h>
#define MILLI 80
#define HZ ((double)1000/(double)MILLI)
/* milli to nano */
#define m2n(a) (a*1000*1000)
void run_target(char *prog)
{
printf("Emulating %.2lf Hz to proccess %s...\n\n", HZ, prog);
if (ptrace(PTRACE_TRACEME, 0, 0, 0) < 0) {
perror("ptrace");
return ;
}
execl(prog, prog, (char*)NULL);
}
void run_emulator(pid_t child)
{
int wait_status;
struct timespec req;
unsigned long int count = 1;
/* set up the emulation speed */
req.tv_sec = 0;
req.tv_nsec = m2n(MILLI);
/* wait for stop on first instruction */
wait(&wait_status);
while (WIFSTOPPED(wait_status)) {
/* this loop will repeat at every instruction, so it executes the
* instruction and sleeps for the amount of time needed to
* emulate the wanted speed.
*/
if (ptrace(PTRACE_SINGLESTEP, child, 0, 0) < 0) {
perror("ptrace");
return ;
}
wait(&wait_status);
/* this does the sleep */
nanosleep(&req, NULL);
}
}
int main(int argc, char *argv[])
{
pid_t child;
if (argc < 2) {
fprintf(stderr, "Usage: %s [prog_name]\n", argv[0]);
exit(EXIT_FAILURE);
}
child = fork();
if (!child)
run_target(argv[1]);
else if (child > 0)
run_emulator(child);
else {
perror("fork");
exit(EXIT_FAILURE);
}
return 0;
}
To make a quick test, I wrote this simple fat(5)
calculator in Assembly, which has 65 instructions (on my machine, of course):
.section .data
.section .text
.globl _start
.globl factorial
_start:
pushq $5
call factorial
movq %rax, %rdi
movq $0x3c, %rax
syscall
.type factorial, @function
factorial:
pushq %rbp
movq %rsp, %rbp
movq 16(%rbp), %rax
cmpq $1, %rax
je end_factorial
decq %rax
pushq %rax
call factorial
movq 16(%rbp), %rbx
imulq %rbx, %rax
end_factorial:
movq %rbp, %rsp
popq %rbp
ret
To assemble, link, run and see the result:
$ as -o fat.o fat.s
$ ld -o fat fat.o
$ ./fat
$ echo $?
120
$
So, it works and computes the factorial of 5. So, if I'm right on the math, 65 instructions will take 65/12.5
seconds to run on a 12.5Hz CPU, right? 65/12.5 = 5.2
.
$ time ./lower ./fat
Emulating 12.50 Hz to proccess ./fat...
Returned: 30720
real 0m5.211s
user 0m0.000s
sys 0m0.008s
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With