sisoputnfrba / foro

Foro de consultas para el trabajo práctico
148 stars 7 forks source link

Problema con serialización: op_code erróneo. #3741

Closed VinixHub closed 4 months ago

VinixHub commented 4 months ago

Buenos días/tardes/noches a todo aquel que haya llegado a este Issue. Como dice el título, tengo este problema, y siendo más específico: Con mi equipo queremos serializar al proceso para mandarlo al CPU, pero cuando llega al CPU no toma el op_code 'PROCESO' (en este intento de serializar no incluimos el path todavía). Estuvimos buen tiempo intentando averiguar cómo saber cuál es el valor de ese 'op_code' al momento de que llega al CPU, pero ninguna nos ayudo la verdad (cabe aclarar que buscamos en el foro y los Issues #3650 y #3662 no nos han ayudado en nuestro problema) Nuestras consultas son:

Código relevante: Creación del proceso:

PCB *crearProceso(int quantum){  // Función para crear los procesos.
    PCB *proceso = malloc(sizeof(PCB));
    if (proceso == NULL) {
        printf("Error al crear el proceso! yo que vos me procupo! jeje");
        return NULL;
    } else {
        proceso->PID = 0; 
        proceso->programCounter = 0;
        proceso->quantum = quantum;
        proceso->registros = iniciarRegistros(); //  Pone todos los registros en 0
        proceso->estado = NEW;
        log_info(loggerKERNEL, "Se crea el proceso <%d> en NEW!", proceso->PID);
        return proceso;
    }
};

Envío del proceso a CPU:

void enviarProcesoCPU(PCB proceso, int socket_cliente){
    t_buffer* bufferProceso = malloc(sizeof(t_buffer));

    bufferProceso->size = sizeof(uint32_t) * 3 + sizeof(proceso.registros) + sizeof(uint8_t);
    bufferProceso->offset = 0;
    bufferProceso->stream = malloc(bufferProceso->size);

    memcpy(bufferProceso->stream + bufferProceso->offset, &proceso.PID, sizeof(uint32_t)); bufferProceso->offset += sizeof(uint32_t);
    memcpy(bufferProceso->stream + bufferProceso->offset, &proceso.quantum, sizeof(uint32_t)); bufferProceso->offset += sizeof(uint32_t);
    memcpy(bufferProceso->stream + bufferProceso->offset, &proceso.programCounter, sizeof(uint32_t)); bufferProceso->offset += sizeof(uint32_t);
    memcpy(bufferProceso->stream + bufferProceso->offset, &proceso.registros, sizeof(proceso.registros)); bufferProceso->offset += sizeof(proceso.registros);
    memcpy(bufferProceso->stream + bufferProceso->offset, &proceso.estado, sizeof(uint8_t)); bufferProceso->offset += sizeof(uint8_t);

    t_paquete* paqueteProceso = malloc(sizeof(t_paquete));
    paqueteProceso->codigo_operacion = PROCESO;
    paqueteProceso->buffer = bufferProceso;
    void* a_enviar = malloc(bufferProceso->size + sizeof(uint8_t) + sizeof(uint32_t));
    int offset = 0;

    memcpy(a_enviar + offset, &(paqueteProceso->codigo_operacion), sizeof(uint8_t)); offset += sizeof(uint8_t);
    memcpy(a_enviar + offset, &(paqueteProceso->buffer->size), sizeof(uint32_t)); offset += sizeof(uint32_t);
    memcpy(a_enviar + offset, paqueteProceso->buffer->stream, paqueteProceso->buffer->size);

    send(socket_cliente, a_enviar, bufferProceso->size + sizeof(uint8_t) + sizeof(uint32_t), 0);

    free(a_enviar); free(paqueteProceso->buffer->stream); free(paqueteProceso->buffer); free(paqueteProceso);
}

Recepción del proceso (en CPU):

void recibirProceso(int cliente_fd) {
    t_paquete* paqueteProceso = malloc(sizeof(t_paquete));
    paqueteProceso->buffer = malloc(sizeof(t_buffer));

    recv(cliente_fd, &(paqueteProceso->codigo_operacion), sizeof(uint8_t), 0);
    recv(cliente_fd, &(paqueteProceso->buffer->size), sizeof(uint32_t), 0);
    paqueteProceso->buffer->stream = malloc(paqueteProceso->buffer->size);
    recv(cliente_fd, paqueteProceso->buffer->stream, paqueteProceso->buffer->size, 0);

    switch (paqueteProceso->codigo_operacion) {
    case PROCESO:
        PCB* proceso = serializarProceso(paqueteProceso->buffer);
        log_info(loggerCPU, "Estado del proceso: %s", buscarEstado(proceso->estado));
        break;
    case -1:
        log_error(loggerCPU, "El cliente se ha ido!");
        break;
    default:
        log_warning(loggerCPU, "El codigo esta mal chavo"); // Siempre tira esta linea -> tristeza absoluta
        break;
    }
}
iago64 commented 4 months ago

Buenas! Cómo va?

Probaron ejecutar el TP con valgrind a ver que les esta llegando? Y mas importante, ¿Esto les pasa si este es el primer mensaje o es algo que les pasa despues de mandar algun mensaje previo? Porque no sea cosa de que esten comiendose el recibir algo y les quede basura en el buffer.

Saludos.-

VinixHub commented 4 months ago

Hola! Respondiendo en orden:

Durante esta hora nos dimos cuenta que usaban eso y lo implementamos. Obtuvimos este código:

==23094== Conditional jump or move depends on uninitialised value(s)
==23094==    at 0x1097D8: recibirProceso (main.c:62)
==23094==    by 0x1098CF: main (main.c:82)
==23094==  Uninitialised value was created by a heap allocation
==23094==    at 0x4848899: malloc (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so)
==23094==    by 0x109737: recibirProceso (main.c:54)
==23094==    by 0x1098CF: main (main.c:82)
==23094== 
==23094== Use of uninitialised value of size 8
==23094==    at 0x1096B1: buscarEstado (main.c:45)
==23094==    by 0x109802: recibirProceso (main.c:65)
==23094==    by 0x1098CF: main (main.c:82)
==23094==  Uninitialised value was created by a heap allocation
==23094==    at 0x4848899: malloc (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so)
==23094==    by 0x1095D0: serializarProceso (main.c:32)
==23094==    by 0x1097F0: recibirProceso (main.c:64)
==23094==    by 0x1098CF: main (main.c:82)
==23094== 
[INFO] 17:49:36:353 CPU/(23094:23094): Estado del proceso: NEW

Si no nos equivocamos, le estamos pasando a la función 'recibirProceso(int cliente_fd)' una variable que no está declarada (nosotros en realidad le pasamos directamente 'recibirProceso(conexionDesdeKernel())'; y esta función nos retorna el int del socket), y los demás errores no los terminamos de entender :(

Y respecto a si es el primer mensaje: sí, lo es.

f-and commented 4 months ago

Buenas Vinix, de ese log que pasaste indican ciertas cosas, para que sepan y no se asusten con el Valgrind: ==23094== Conditional jump or move depends on uninitialised value(s) ==23094== at 0x1097D8: recibirProceso (main.c:62) Aca estan usando algo en la linea 62 del archivo main.c que no fue inicializado, eso del conexionDesdeKernel() me generó dudas, es una función que llaman para que te retorne el valor del socket? No podrian tener el socket directamente?

==23094== Use of uninitialised value of size 8 ==23094== at 0x1096B1: buscarEstado (main.c:45) Aca estan usando un valor sin inicializar de tamaño 8 que fue inicializado: ==23094== by 0x1095D0: serializarProceso (main.c:32) Osea en la linea 32 en la función serializarProceso.

En cuanto al problema original, otro consejo que podria recomendar es que se fijen paso por paso como lo estan enviando, porque me hacen ruido los 3 recv a un send jajaja.

VinixHub commented 4 months ago

Hola f-and! Te respondo en orden de las preguntas/aclaraciones:

Igualmente quería hacer otro agregado, más que nada algo que nos está realmente confundiendo. Nosotros al hacer el valgrind nos tira el estado del proceso igualmente:

==23094==    by 0x1098CF: main (main.c:82)
==23094== 
[INFO] 17:49:36:353 CPU/(23094:23094): Estado del proceso: NEW

por qué si ejecutamos el valgrind nos detecta el 'case: PROCESO' y cuando no, no lo detecta?

f-and commented 4 months ago

A ver:

Es exactamente como decís hace eso nuestra función; hace la conexión y nos retorna el valor del socket, en este caso del kernel. Con 'tener el socket directamente' a qué se refiere? porque está función ya nos retorna directamente el socket en vez de ponerla en una variable.

Esto me genera mas ruido jajaja, como hace la conexión? Porque para empezar la idea seria como el TP0, que se conecten y se mantengan conectados hasta que se terminen los procesos, seria raro que este conectandose y desconectandose continuamente, por eso digo de tener ya el socket a mano, una vez que se conectan pasan el socket directamente por variable y no necesitan llamar a una función. Además pregunto para saber, como manejarian las dos conexiones que necesitan hacer entre el CPU y el Kernel? (Dispatch e Interrupt)

Cómo podemos hacer para ir viendo paso por paso lo que mandamos? de ser con debugger, lo podemos hacer de otra manera? (no es que odiemos al debugger, solo por curiosidad).

Tristemente no tienen mucha alternativa, sobre todo cuando empiezan a manejar hilos ya se vuelve muy dificil ver un paso a paso, mi mejor recomendación es usar a full el log_debug() de las commons para poder ver que es lo que esta haciendo en cada momento, total despues lo ocultan para la entrega y nosotros ni enterados ;) Sino tienen el GDB que tiene ciertas formas de ver los hilos pero es un toque engorroso.

Por último; el uso de los 3 recv queremos creer están bien, pero porque previo a hacer el paso de un proceso de Kernel a CPU, hicimos el de un tipo 'Persona' (el de la guía de serialización) y nos había llegado sin ningún problema.

Esto ya es nitpick mio, no es una cosa mala necesariamente, pero a lo que iba era mas fijarse como esta llegando individualmente cada uno de los recv anteriores y siguientes para ver si hay algo que se esta rompiendo en el proceso o te queda colgado de otra cosa.

por qué si ejecutamos el valgrind nos detecta el 'case: PROCESO' y cuando no, no lo detecta?

Valgrind a veces tira magia y te arregla algunos problemas de memoria, o te inicializa cosas. Puede ser eso o puede ser que hayan arreglado algo jaja.

VinixHub commented 4 months ago

Hola de novo! Posta gracias f-and por respondernos. Nuevamente te respondo en orden:

iago64 commented 4 months ago

Buenas! Cómo va?

Me toca ser el de las malas noticias, Valgrind es mucho muy posible que les este arreglando un par de cagadas con la memoria. Una forma fácil de saber se la mandaron con la memoria es el revisar si en el log de valgrind les aparecen Invalid Read o Invalid Write of size o si tienen Conditional jump or move depends on uninitialised value(s) ya que esos errores son los que suelen estar asociados a cagadas en la memoria.

Por otro lado, es fundamental que revisen si los mensajes que mandan, los estan recibiendo completos del otro lado, es decir, si uds mandan 30 bytes, del otro lado entre la suma de todos los recv() tienen que sumar esos 30 bytes, si no, les puede estar quedando basura en el buffer y eso hace que después todo se les corra un toque y bueno, pasen estas cosas de que intepretan cualquiera, por las dudas usando el debugger revisen eso a ver si están haciendo bien los send() y los recv()

Saludos.-

VinixHub commented 4 months ago

HOLAAAAAAAAAAAAA!!! perdón las mayúsculas, pero lo pudimos arreglar! Muchísimas gracias a ambos por su tiempo y dedicación. La solución vino de la mano de iago64, en específico con esto que escribiste: Valgrind es mucho muy posible que les este arreglando un par de cagadas con la memoria. Una forma fácil de saber se la mandaron con la memoria es el revisar si en el log de valgrind les aparecen Invalid Read o Invalid Write of size o si tienen Conditional jump or move depends on uninitialised value(s) ya que esos errores son los que suelen estar asociados a cagadas en la memoria. Leí eso y dije: 'banca, yo me estoy fijando el valor del op_code, pero y de lo demás? y resulta que cuando inicio el paquete del lado del CPU el size tenía 61124 (????) y lo inicialicé como debía y me anduvo! no te puedo creer... De nuevo, de parte de los 5 muchas gracias a ambos por su tiempo y dedicación (si están cuando les debamos entregar el TP háganoslo saber y les dropeamos unas facturitas como agradecimiento jeje). Cierro el Issue!