diff options
-rw-r--r-- | base.h | 81 | ||||
-rw-r--r-- | data.c | 163 | ||||
-rw-r--r-- | main.c | 458 | ||||
-rw-r--r-- | memory.c | 104 | ||||
-rw-r--r-- | ui.glade | 777 | ||||
-rw-r--r-- | unicode.h | 1 |
6 files changed, 1023 insertions, 561 deletions
@@ -0,0 +1,81 @@ +// types and stuff needed everywhere + +#include <gtk/gtk.h> +#include <fcntl.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdbool.h> +#include <inttypes.h> +#include <unistd.h> +#include <assert.h> +#include <ctype.h> +#include <wctype.h> + +typedef pid_t PID; +typedef uint64_t Address; +#define SCNxADDR SCNx64 +#define PRIdADDR PRId64 +#define PRIxADDR PRIx64 + +typedef enum { + TYPE_U8, + TYPE_S8, + TYPE_U16, + TYPE_S16, + TYPE_U32, + TYPE_S32, + TYPE_U64, + TYPE_S64, + TYPE_ASCII, + TYPE_UTF16, + TYPE_UTF32, + TYPE_F32, + TYPE_F64 +} DataType; + +typedef enum { + SEARCH_ENTER_VALUE, + SEARCH_SAME_DIFFERENT +} SearchType; + +// a memory map +typedef struct { + Address lo, size; +} Map; + +typedef struct { + GtkWindow *window; + GtkBuilder *builder; + bool stop_while_accessing_memory; + long editing_memory; // index of memory value being edited, or -1 if none is + PID pid; + Address total_memory; // total amount of memory used by process, in bytes + Map *maps; + Address memory_view_address; + uint32_t memory_view_entries; // # of entries to show + unsigned nmaps; + DataType data_type; + SearchType search_type; +} State; + +static void display_dialog_box_nofmt(State *state, GtkMessageType type, char const *message) { + GtkWidget *box = gtk_message_dialog_new(state->window, + GTK_DIALOG_MODAL|GTK_DIALOG_DESTROY_WITH_PARENT, + type, GTK_BUTTONS_OK, "%s", message); + // make sure dialog box is closed when OK is clicked. + g_signal_connect_swapped(box, "response", G_CALLBACK(gtk_widget_destroy), box); + gtk_widget_show_all(box); +} + +// this is a macro so we get -Wformat warnings +#define display_dialog_box(state, type, fmt, ...) do { \ + char _buf[1024]; \ + snprintf(_buf, sizeof _buf, fmt, __VA_ARGS__); \ + display_dialog_box_nofmt(state, type, _buf); \ +} while (0) +#define display_error(state, fmt, ...) display_dialog_box(state, GTK_MESSAGE_ERROR, fmt, __VA_ARGS__) +#define display_info(state, fmt, ...) display_dialog_box(state, GTK_MESSAGE_INFO, fmt, __VA_ARGS__) +#define display_info_nofmt(state, message) display_dialog_box_nofmt(state, GTK_MESSAGE_INFO, message) + + @@ -0,0 +1,163 @@ +// stuff for dealing with various data types. + +static DataType data_type_from_name(char const *name) { + switch (name[0]) { + case 'u': + switch (atoi(&name[1])) { + case 8: return TYPE_U8; + case 16: return TYPE_U16; + case 32: return TYPE_U32; + case 64: return TYPE_U64; + } + if (strncmp(name, "utf", 3) == 0) { + switch (atoi(&name[3])) { + case 16: return TYPE_UTF16; + case 32: return TYPE_UTF32; + } + } + break; + case 's': + switch (atoi(&name[1])) { + case 8: return TYPE_S8; + case 16: return TYPE_S16; + case 32: return TYPE_S32; + case 64: return TYPE_S64; + } + break; + case 'f': + switch (atoi(&name[1])) { + case 32: return TYPE_F32; + case 64: return TYPE_F64; + } + break; + case 'a': + if (strcmp(name, "ascii") == 0) + return TYPE_ASCII; + break; + } + assert(0); + return TYPE_U8; +} + +static size_t data_type_size(DataType type) { + switch (type) { + case TYPE_U8: + case TYPE_S8: + case TYPE_ASCII: + return 1; + case TYPE_U16: + case TYPE_S16: + case TYPE_UTF16: + return 2; + case TYPE_U32: + case TYPE_S32: + case TYPE_F32: + case TYPE_UTF32: + return 4; + case TYPE_U64: + case TYPE_S64: + case TYPE_F64: + return 8; + } + return (size_t)-1; +} + +// set str to "a" for 'a', "\\n" for '\n', "\\xff" for (wchar_t)255, etc. +static void char_to_str(uint32_t c, char *str, size_t str_size) { + if (c <= WINT_MAX && iswgraph((wint_t)c)) { + snprintf(str, str_size, "%lc", (wint_t)c); + } else { + switch (c) { + case ' ': snprintf(str, str_size, "(space)"); break; + case '\n': snprintf(str, str_size, "\\n"); break; + case '\t': snprintf(str, str_size, "\\t"); break; + case '\r': snprintf(str, str_size, "\\r"); break; + case '\v': snprintf(str, str_size, "\\v"); break; + case '\0': snprintf(str, str_size, "\\0"); break; + default: + if (c < 256) + snprintf(str, str_size, "\\x%02x", (unsigned)c); + else + snprintf(str, str_size, "\\x%05lx", (unsigned long)c); + } + } +} + +static bool char_from_str(char const *str, uint32_t *c) { + if (str[0] == '\0') return false; + if (str[0] == '\\') { + switch (str[1]) { + case 'n': *c = '\n'; return str[2] == '\0'; + case 't': *c = '\t'; return str[2] == '\0'; + case 'r': *c = '\r'; return str[2] == '\0'; + case 'v': *c = '\v'; return str[2] == '\0'; + case '0': *c = '\0'; return str[2] == '\0'; + case 'x': { + unsigned long v = 0; + int w = 0; + if (sscanf(&str[2], "%lx%n", &v, &w) != 1 || + (size_t)w != strlen(&str[2]) || v > UINT32_MAX) + return false; + *c = (uint32_t)v; + return true; + } + } + } + return unicode_utf8_to_utf32(c, str, strlen(str)) == strlen(str); +} + +static void data_to_str(void const *value, DataType type, char *str, size_t str_size) { + switch (type) { + case TYPE_U8: snprintf(str, str_size, "%" PRIu8, *(uint8_t *)value); break; + case TYPE_U16: snprintf(str, str_size, "%" PRIu16, *(uint16_t *)value); break; + case TYPE_U32: snprintf(str, str_size, "%" PRIu32, *(uint32_t *)value); break; + case TYPE_U64: snprintf(str, str_size, "%" PRIu64, *(uint64_t *)value); break; + case TYPE_S8: snprintf(str, str_size, "%" PRId8, *(int8_t *)value); break; + case TYPE_S16: snprintf(str, str_size, "%" PRId16, *(int16_t *)value); break; + case TYPE_S32: snprintf(str, str_size, "%" PRId32, *(int32_t *)value); break; + case TYPE_S64: snprintf(str, str_size, "%" PRId64, *(int64_t *)value); break; + case TYPE_F32: snprintf(str, str_size, "%g", *(float *)value); break; + case TYPE_F64: snprintf(str, str_size, "%g", *(double *)value); break; + case TYPE_UTF16: char_to_str(*(uint16_t *)value, str, str_size); break; + case TYPE_UTF32: char_to_str(*(uint32_t *)value, str, str_size); break; + case TYPE_ASCII: + char_to_str((uint8_t)*(char *)value, str, str_size); + break; + + } +} + +// returns true on success, false if str is not a well-formatted value +static bool data_from_str(char const *str, DataType type, void *value) { + int len = (int)strlen(str); + int w = 0; + uint32_t c = 0; + switch (type) { + case TYPE_U8: return sscanf(str, "%" SCNu8 "%n", (uint8_t *)value, &w) == 1 && w == len; + case TYPE_S8: return sscanf(str, "%" SCNd8 "%n", ( int8_t *)value, &w) == 1 && w == len; + case TYPE_U16: return sscanf(str, "%" SCNu16 "%n", (uint16_t *)value, &w) == 1 && w == len; + case TYPE_S16: return sscanf(str, "%" SCNd16 "%n", ( int16_t *)value, &w) == 1 && w == len; + case TYPE_U32: return sscanf(str, "%" SCNu32 "%n", (uint32_t *)value, &w) == 1 && w == len; + case TYPE_S32: return sscanf(str, "%" SCNd32 "%n", ( int32_t *)value, &w) == 1 && w == len; + case TYPE_U64: return sscanf(str, "%" SCNu64 "%n", (uint64_t *)value, &w) == 1 && w == len; + case TYPE_S64: return sscanf(str, "%" SCNd64 "%n", ( int64_t *)value, &w) == 1 && w == len; + case TYPE_F32: return sscanf(str, "%f%n", (float *)value, &w) == 1 && w == len; + case TYPE_F64: return sscanf(str, "%lf%n", (double *)value, &w) == 1 && w == len; + case TYPE_ASCII: + if (!char_from_str(str, &c)) return false; + if (c > 127) return false; + *(uint8_t *)value = (uint8_t)c; + return true; + case TYPE_UTF16: + if (!char_from_str(str, &c)) return false; + if (c > 65535) return false; + *(uint16_t *)value = (uint16_t)c; + return true; + case TYPE_UTF32: + if (!char_from_str(str, &c)) return false; + *(uint32_t *)value = c; + return true; + } + assert(0); + return false; +} @@ -1,341 +1,16 @@ -#include <gtk/gtk.h> -#include <fcntl.h> -#include <errno.h> -#include <stdio.h> -#include <stdlib.h> -#include <stdbool.h> -#include <inttypes.h> -#include <unistd.h> -#include <assert.h> -#include <ctype.h> -#include <wctype.h> - -typedef pid_t PID; -typedef uint64_t Address; -#define SCNxADDR SCNx64 -#define PRIdADDR PRId64 -#define PRIxADDR PRIx64 - +#include "base.h" #include "unicode.h" - -// a memory map -typedef struct { - Address lo, size; -} Map; - -typedef enum { - TYPE_U8, - TYPE_S8, - TYPE_U16, - TYPE_S16, - TYPE_U32, - TYPE_S32, - TYPE_U64, - TYPE_S64, - TYPE_ASCII, - TYPE_UTF16, - TYPE_UTF32, - TYPE_F32, - TYPE_F64 -} DataType; - -static DataType data_type_from_name(char const *name) { - switch (name[0]) { - case 'u': - switch (atoi(&name[1])) { - case 8: return TYPE_U8; - case 16: return TYPE_U16; - case 32: return TYPE_U32; - case 64: return TYPE_U64; - } - if (strncmp(name, "utf", 3) == 0) { - switch (atoi(&name[3])) { - case 16: return TYPE_UTF16; - case 32: return TYPE_UTF32; - } - } - break; - case 's': - switch (atoi(&name[1])) { - case 8: return TYPE_S8; - case 16: return TYPE_S16; - case 32: return TYPE_S32; - case 64: return TYPE_S64; - } - break; - case 'f': - switch (atoi(&name[1])) { - case 32: return TYPE_F32; - case 64: return TYPE_F64; - } - break; - case 'a': - if (strcmp(name, "ascii") == 0) - return TYPE_ASCII; - break; - } - assert(0); - return TYPE_U8; -} - -static size_t data_type_size(DataType type) { - switch (type) { - case TYPE_U8: - case TYPE_S8: - case TYPE_ASCII: - return 1; - case TYPE_U16: - case TYPE_S16: - case TYPE_UTF16: - return 2; - case TYPE_U32: - case TYPE_S32: - case TYPE_F32: - case TYPE_UTF32: - return 4; - case TYPE_U64: - case TYPE_S64: - case TYPE_F64: - return 8; - } - return (size_t)-1; -} - -// set str to "a" for 'a', "\\n" for '\n', "\\xff" for (wchar_t)255, etc. -static void char_to_str(uint32_t c, char *str, size_t str_size) { - if (c <= WINT_MAX && iswgraph((wint_t)c)) { - snprintf(str, str_size, "%lc", (wint_t)c); - } else { - switch (c) { - case ' ': snprintf(str, str_size, "(space)"); break; - case '\n': snprintf(str, str_size, "\\n"); break; - case '\t': snprintf(str, str_size, "\\t"); break; - case '\r': snprintf(str, str_size, "\\r"); break; - case '\v': snprintf(str, str_size, "\\v"); break; - case '\0': snprintf(str, str_size, "\\0"); break; - default: - if (c < 256) - snprintf(str, str_size, "\\x%02x", (unsigned)c); - else - snprintf(str, str_size, "\\x%05lx", (unsigned long)c); - } - } -} - -static bool char_from_str(char const *str, uint32_t *c) { - if (str[0] == '\0') return false; - if (str[0] == '\\') { - switch (str[1]) { - case 'n': *c = '\n'; return str[2] == '\0'; - case 't': *c = '\t'; return str[2] == '\0'; - case 'r': *c = '\r'; return str[2] == '\0'; - case 'v': *c = '\v'; return str[2] == '\0'; - case '0': *c = '\0'; return str[2] == '\0'; - case 'x': { - unsigned long v = 0; - int w = 0; - if (sscanf(&str[2], "%lx%n", &v, &w) != 1 || - (size_t)w != strlen(&str[2]) || v > UINT32_MAX) - return false; - *c = (uint32_t)v; - return true; - } - } - } - return unicode_utf8_to_utf32(c, str, strlen(str)) == strlen(str); -} - -static void data_to_str(void const *value, DataType type, char *str, size_t str_size) { - switch (type) { - case TYPE_U8: snprintf(str, str_size, "%" PRIu8, *(uint8_t *)value); break; - case TYPE_U16: snprintf(str, str_size, "%" PRIu16, *(uint16_t *)value); break; - case TYPE_U32: snprintf(str, str_size, "%" PRIu32, *(uint32_t *)value); break; - case TYPE_U64: snprintf(str, str_size, "%" PRIu64, *(uint64_t *)value); break; - case TYPE_S8: snprintf(str, str_size, "%" PRId8, *(int8_t *)value); break; - case TYPE_S16: snprintf(str, str_size, "%" PRId16, *(int16_t *)value); break; - case TYPE_S32: snprintf(str, str_size, "%" PRId32, *(int32_t *)value); break; - case TYPE_S64: snprintf(str, str_size, "%" PRId64, *(int64_t *)value); break; - case TYPE_F32: snprintf(str, str_size, "%g", *(float *)value); break; - case TYPE_F64: snprintf(str, str_size, "%g", *(double *)value); break; - case TYPE_UTF16: char_to_str(*(uint16_t *)value, str, str_size); break; - case TYPE_UTF32: char_to_str(*(uint32_t *)value, str, str_size); break; - case TYPE_ASCII: - char_to_str((uint8_t)*(char *)value, str, str_size); - break; - - } -} - -// returns true on success, false if str is not a well-formatted value -static bool data_from_str(char const *str, DataType type, void *value) { - int len = (int)strlen(str); - int w = 0; - uint32_t c = 0; - switch (type) { - case TYPE_U8: return sscanf(str, "%" SCNu8 "%n", (uint8_t *)value, &w) == 1 && w == len; - case TYPE_S8: return sscanf(str, "%" SCNd8 "%n", ( int8_t *)value, &w) == 1 && w == len; - case TYPE_U16: return sscanf(str, "%" SCNu16 "%n", (uint16_t *)value, &w) == 1 && w == len; - case TYPE_S16: return sscanf(str, "%" SCNd16 "%n", ( int16_t *)value, &w) == 1 && w == len; - case TYPE_U32: return sscanf(str, "%" SCNu32 "%n", (uint32_t *)value, &w) == 1 && w == len; - case TYPE_S32: return sscanf(str, "%" SCNd32 "%n", ( int32_t *)value, &w) == 1 && w == len; - case TYPE_U64: return sscanf(str, "%" SCNu64 "%n", (uint64_t *)value, &w) == 1 && w == len; - case TYPE_S64: return sscanf(str, "%" SCNd64 "%n", ( int64_t *)value, &w) == 1 && w == len; - case TYPE_F32: return sscanf(str, "%f%n", (float *)value, &w) == 1 && w == len; - case TYPE_F64: return sscanf(str, "%lf%n", (double *)value, &w) == 1 && w == len; - case TYPE_ASCII: - if (!char_from_str(str, &c)) return false; - if (c > 127) return false; - *(uint8_t *)value = (uint8_t)c; - return true; - case TYPE_UTF16: - if (!char_from_str(str, &c)) return false; - if (c > 65535) return false; - *(uint16_t *)value = (uint16_t)c; - return true; - case TYPE_UTF32: - if (!char_from_str(str, &c)) return false; - *(uint32_t *)value = c; - return true; +#include "data.c" +#include "memory.c" + +static SearchType search_type_from_str(char const *str) { + if (strcmp(str, "enter-value") == 0) { + return SEARCH_ENTER_VALUE; + } else if (strcmp(str, "same-different") == 0) { + return SEARCH_SAME_DIFFERENT; } assert(0); - return false; -} - -typedef struct { - GtkWindow *window; - GtkBuilder *builder; - bool stop_while_accessing_memory; - long editing_memory; // index of memory value being edited, or -1 if none is - PID pid; - Map *maps; - Address memory_view_address; - uint32_t memory_view_entries; // # of entries to show - unsigned nmaps; - DataType data_type; -} State; - -static void display_dialog_box_nofmt(State *state, GtkMessageType type, char const *message) { - GtkWidget *box = gtk_message_dialog_new(state->window, - GTK_DIALOG_MODAL|GTK_DIALOG_DESTROY_WITH_PARENT, - type, GTK_BUTTONS_OK, "%s", message); - // make sure dialog box is closed when OK is clicked. - g_signal_connect_swapped(box, "response", G_CALLBACK(gtk_widget_destroy), box); - gtk_widget_show_all(box); -} - -// this is a macro so we get -Wformat warnings -#define display_dialog_box(state, type, fmt, ...) do { \ - char _buf[1024]; \ - snprintf(_buf, sizeof _buf, fmt, __VA_ARGS__); \ - display_dialog_box_nofmt(state, type, _buf); \ -} while (0) -#define display_error(state, fmt, ...) display_dialog_box(state, GTK_MESSAGE_ERROR, fmt, __VA_ARGS__) -#define display_info(state, fmt, ...) display_dialog_box(state, GTK_MESSAGE_INFO, fmt, __VA_ARGS__) -#define display_info_nofmt(state, message) display_dialog_box_nofmt(state, GTK_MESSAGE_INFO, message) - - - -static void close_process(State *state, char const *reason) { - GtkBuilder *builder = state->builder; - free(state->maps); state->maps = NULL; - state->nmaps = 0; - state->pid = 0; - { - GtkListStore *store = GTK_LIST_STORE(gtk_builder_get_object(builder, "memory")); - gtk_list_store_clear(store); - } - if (reason) - display_info(state, "Can't access process anymore: %s", reason); - else - display_info_nofmt(state, "Can't access process anymore."); -} - -// don't use this function; use one of the ones below -static int memory_open(State *state, int flags) { - if (state->pid) { - if (state->stop_while_accessing_memory) { - if (kill(state->pid, SIGSTOP) == -1) { - close_process(state, strerror(errno)); - return 0; - } - } - char name[64]; - sprintf(name, "/proc/%lld/mem", (long long)state->pid); - int fd = open(name, flags); - if (fd == -1) { - close_process(state, strerror(errno)); - return 0; - } - return fd; - } - return 0; -} - -static void memory_close(State *state, int fd) { - if (state->stop_while_accessing_memory) { - kill(state->pid, SIGCONT); - } - if (fd) close(fd); -} - - -// get a file descriptor for reading memory from the process -// returns 0 on failure -static int memory_reader_open(State *state) { - return memory_open(state, O_RDONLY); -} - -static void memory_reader_close(State *state, int reader) { - memory_close(state, reader); -} - -// like memory_reader_open, but for writing to memory -static int memory_writer_open(State *state) { - return memory_open(state, O_WRONLY); -} - -static void memory_writer_close(State *state, int writer) { - memory_close(state, writer); -} - -static uint8_t memory_read_byte(int reader, Address addr) { - lseek(reader, (off_t)addr, SEEK_SET); - uint8_t byte = 0; - read(reader, &byte, 1); - return byte; -} - -// returns number of bytes successfully read -static Address memory_read_bytes(int reader, Address addr, uint8_t *memory, Address nbytes) { - lseek(reader, (off_t)addr, SEEK_SET); - Address idx = 0; - while (idx < nbytes) { - ssize_t n = read(reader, &memory[idx], (size_t)(nbytes - idx)); - if (n <= 0) break; - idx += (Address)n; - } - return idx; -} - -// returns # of bytes written (so either 0 or 1) -static Address memory_write_byte(int writer, Address addr, uint8_t byte) { - lseek(writer, (off_t)addr, SEEK_SET); - if (write(writer, &byte, 1) == 1) - return 1; - else - return 0; -} - -// returns # of bytes written -static Address memory_write_bytes(int writer, Address addr, uint8_t const *bytes, Address nbytes) { - lseek(writer, (off_t)addr, SEEK_SET); - Address idx = 0; - while (idx < nbytes) { - ssize_t n = write(writer, &bytes[idx], (size_t)(nbytes - idx)); - if (n < 0) break; - idx += (Address)n; - } - return idx; + return 0xff; } // pass config_potentially_changed = false if there definitely hasn't been an update to the target address @@ -383,7 +58,34 @@ static void update_memory_view(State *state, bool config_potentially_changed) { } } -G_MODULE_EXPORT void update_configuration(GtkWidget *widget, gpointer user_data) { +static void bytes_to_text(uint64_t nbytes, char *out, size_t out_size) { + if (nbytes == 0) + snprintf(out, out_size, "0"); + else if (nbytes < (1ul<<10)) + snprintf(out, out_size, "%uB", (unsigned)nbytes); + else if (nbytes < (1ul<<20)) + snprintf(out, out_size, "%.1fKB", (double)nbytes / (double)(1ul<<10)); + else if (nbytes < (1ul<<30)) + snprintf(out, out_size, "%.1fMB", (double)nbytes / (double)(1ul<<20)); + else + snprintf(out, out_size, "%.1fGB", (double)nbytes / (double)(1ul<<30)); +} + +// returns the name of the selected GtkRadioButton in the group whose leader's name is group_name +// (or NULL if none is, which shouldn't usually happen) +static char const *radio_group_get_selected(State *state, char const *group_name) { + GtkRadioButton *leader = GTK_RADIO_BUTTON(gtk_builder_get_object(state->builder, group_name)); + for (GSList *l = gtk_radio_button_get_group(leader); + l; l = l->next) { + GtkToggleButton *button = l->data; + if (gtk_toggle_button_get_active(button)) { + return gtk_widget_get_name(GTK_WIDGET(button)); + } + } + return NULL; +} + +G_MODULE_EXPORT void update_configuration(GtkWidget *_widget, gpointer user_data) { State *state = user_data; GtkBuilder *builder = state->builder; state->stop_while_accessing_memory = gtk_toggle_button_get_active( @@ -404,19 +106,48 @@ G_MODULE_EXPORT void update_configuration(GtkWidget *widget, gpointer user_data) state->memory_view_address = address; update_memview = true; } - GtkRadioButton *data_type_u8 = GTK_RADIO_BUTTON( - gtk_builder_get_object(builder, "type-u8")); - for (GSList *l = gtk_radio_button_get_group(data_type_u8); - l; l = l->next) { - GtkToggleButton *button = l->data; - if (gtk_toggle_button_get_active(button)) { - char const *type_name = gtk_widget_get_name(GTK_WIDGET(button)); - DataType type = data_type_from_name(type_name); - if (state->data_type != type) { - state->data_type = type; - update_memview = true; - } + char const *data_type_str = radio_group_get_selected(state, "type-u8"); + DataType data_type = data_type_from_name(data_type_str); + if (state->data_type != data_type) { + state->data_type = data_type; + update_memview = true; + } + + // update memory/disk estimates for search + GtkLabel *memory_label = GTK_LABEL(gtk_builder_get_object(builder, "required-memory")); + GtkLabel *disk_label = GTK_LABEL(gtk_builder_get_object(builder, "required-disk")); + char const *search_type_str = radio_group_get_selected(state, "enter-value"); + SearchType search_type = search_type_from_str(search_type_str); + state->search_type = search_type; + if (state->pid) { + size_t item_size = data_type_size(data_type); + Address total_memory = state->total_memory; + Address total_items = total_memory / item_size; + Address memory_usage = total_items / 8; // 1 bit per item + { + char text[32]; + bytes_to_text(memory_usage, text, sizeof text); + gtk_label_set_text(memory_label, text); + } + Address disk_usage = 0; + switch (search_type) { + case SEARCH_ENTER_VALUE: + // no disk space needed + break; + case SEARCH_SAME_DIFFERENT: + // we need to store all of memory on disk for this kind of search + disk_usage = total_memory; + break; } + + { + char text[32]; + bytes_to_text(disk_usage, text, sizeof text); + gtk_label_set_text(disk_label, text); + } + } else { + gtk_label_set_text(memory_label, "N/A"); + gtk_label_set_text(disk_label, "N/A"); } if (update_memview) { @@ -439,6 +170,7 @@ static void update_maps(State *state) { rewind(maps_file); Map *maps = state->maps = calloc(capacity, sizeof *maps); unsigned nmaps = 0; + state->total_memory = 0; if (maps) { while (fgets(line, sizeof line, maps_file)) { Address addr_lo, addr_hi; @@ -448,6 +180,7 @@ static void update_maps(State *state) { Map *map = &maps[nmaps++]; map->lo = addr_lo; map->size = addr_hi - addr_lo; + state->total_memory += map->size; } } } @@ -461,7 +194,7 @@ static void update_maps(State *state) { } // the user entered a PID. -G_MODULE_EXPORT void select_pid(GtkButton *button, gpointer user_data) { +G_MODULE_EXPORT void select_pid(GtkButton *_button, gpointer user_data) { State *state = user_data; GtkBuilder *builder = state->builder; GtkEntry *pid = GTK_ENTRY(gtk_builder_get_object(builder, "pid")); @@ -491,6 +224,7 @@ G_MODULE_EXPORT void select_pid(GtkButton *button, gpointer user_data) { state->pid = (PID)pid_number; close(dir); update_maps(state); + update_configuration(NULL, state); // we need to do this to update the search resource usage estimates if (state->nmaps) { // display whatever's in the first mapping Map *first_map = &state->maps[0]; @@ -502,7 +236,7 @@ G_MODULE_EXPORT void select_pid(GtkButton *button, gpointer user_data) { } // a value in memory was edited -G_MODULE_EXPORT void memory_edited(GtkCellRendererText *renderer, char *path, char *new_text, gpointer user_data) { +G_MODULE_EXPORT void memory_edited(GtkCellRendererText *_renderer, char *path, char *new_text, gpointer user_data) { State *state = user_data; GtkBuilder *builder = state->builder; DataType data_type = state->data_type; @@ -514,12 +248,14 @@ G_MODULE_EXPORT void memory_edited(GtkCellRendererText *renderer, char *path, ch if (data_from_str(new_text, data_type, &value)) { int writer = memory_writer_open(state); if (writer) { + // write the value bool success = memory_write_bytes(writer, addr, (uint8_t const *)&value, item_size) == item_size; memory_writer_close(state, writer); if (success) { GtkListStore *store = GTK_LIST_STORE(gtk_builder_get_object(builder, "memory")); GtkTreeIter iter; char value_str[32]; + // convert back to a string (so new_text = "0.10" becomes value_str = "0.1", etc.) data_to_str(&value, data_type, value_str, sizeof value_str); gtk_tree_model_get_iter_from_string(GTK_TREE_MODEL(store), &iter, path); gtk_list_store_set(store, &iter, 2, value_str, -1); @@ -529,21 +265,37 @@ G_MODULE_EXPORT void memory_edited(GtkCellRendererText *renderer, char *path, ch } } -G_MODULE_EXPORT void refresh_memory(GtkWidget *widget, gpointer user_data) { +G_MODULE_EXPORT void refresh_memory(GtkWidget *_widget, gpointer user_data) { State *state = user_data; update_memory_view(state, true); } -G_MODULE_EXPORT void memory_editing_started(GtkCellRenderer *renderer, GtkCellEditable *editable, char *path, gpointer user_data) { +G_MODULE_EXPORT void memory_editing_started(GtkCellRenderer *_renderer, GtkCellEditable *editable, char *path, gpointer user_data) { State *state = user_data; state->editing_memory = atol(path); } -G_MODULE_EXPORT void memory_editing_canceled(GtkCellRenderer *renderer, gpointer user_data) { +G_MODULE_EXPORT void memory_editing_canceled(GtkCellRenderer *_renderer, gpointer user_data) { State *state = user_data; state->editing_memory = -1; } +G_MODULE_EXPORT void search_start(GtkWidget *_widget, gpointer user_data) { + State *state = user_data; + GtkBuilder *builder = state->builder; + SearchType search_type = state->search_type; + gtk_widget_hide(GTK_WIDGET(gtk_builder_get_object(builder, "pre-search"))); + gtk_widget_show(GTK_WIDGET(gtk_builder_get_object(builder, "search-common"))); + switch (search_type) { + case SEARCH_ENTER_VALUE: + gtk_widget_show(GTK_WIDGET(gtk_builder_get_object(builder, "search-enter-value"))); + break; + case SEARCH_SAME_DIFFERENT: + gtk_widget_show(GTK_WIDGET(gtk_builder_get_object(builder, "search-same-different"))); + break; + } +} + // this function is run once per frame static gboolean frame_callback(gpointer user_data) { State *state = user_data; diff --git a/memory.c b/memory.c new file mode 100644 index 0000000..37b9983 --- /dev/null +++ b/memory.c @@ -0,0 +1,104 @@ +// low-level stuff for reading/writing the memory of another process + +static void close_process(State *state, char const *reason) { + GtkBuilder *builder = state->builder; + free(state->maps); state->maps = NULL; + state->nmaps = 0; + state->pid = 0; + { + GtkListStore *store = GTK_LIST_STORE(gtk_builder_get_object(builder, "memory")); + gtk_list_store_clear(store); + } + if (reason) + display_info(state, "Can't access process anymore: %s", reason); + else + display_info_nofmt(state, "Can't access process anymore."); +} + +// don't use this function; use one of the ones below +static int memory_open(State *state, int flags) { + if (state->pid) { + if (state->stop_while_accessing_memory) { + if (kill(state->pid, SIGSTOP) == -1) { + close_process(state, strerror(errno)); + return 0; + } + } + char name[64]; + sprintf(name, "/proc/%lld/mem", (long long)state->pid); + int fd = open(name, flags); + if (fd == -1) { + close_process(state, strerror(errno)); + return 0; + } + return fd; + } + return 0; +} + +static void memory_close(State *state, int fd) { + if (state->stop_while_accessing_memory) { + kill(state->pid, SIGCONT); + } + if (fd) close(fd); +} + + +// get a file descriptor for reading memory from the process +// returns 0 on failure +static int memory_reader_open(State *state) { + return memory_open(state, O_RDONLY); +} + +static void memory_reader_close(State *state, int reader) { + memory_close(state, reader); +} + +// like memory_reader_open, but for writing to memory +static int memory_writer_open(State *state) { + return memory_open(state, O_WRONLY); +} + +static void memory_writer_close(State *state, int writer) { + memory_close(state, writer); +} + +static uint8_t memory_read_byte(int reader, Address addr) { + lseek(reader, (off_t)addr, SEEK_SET); + uint8_t byte = 0; + read(reader, &byte, 1); + return byte; +} + +// returns number of bytes successfully read +static Address memory_read_bytes(int reader, Address addr, uint8_t *memory, Address nbytes) { + lseek(reader, (off_t)addr, SEEK_SET); + Address idx = 0; + while (idx < nbytes) { + ssize_t n = read(reader, &memory[idx], (size_t)(nbytes - idx)); + if (n <= 0) break; + idx += (Address)n; + } + return idx; +} + +// returns # of bytes written (so either 0 or 1) +static Address memory_write_byte(int writer, Address addr, uint8_t byte) { + lseek(writer, (off_t)addr, SEEK_SET); + if (write(writer, &byte, 1) == 1) + return 1; + else + return 0; +} + +// returns # of bytes written +static Address memory_write_bytes(int writer, Address addr, uint8_t const *bytes, Address nbytes) { + lseek(writer, (off_t)addr, SEEK_SET); + Address idx = 0; + while (idx < nbytes) { + ssize_t n = write(writer, &bytes[idx], (size_t)(nbytes - idx)); + if (n < 0) break; + idx += (Address)n; + } + return idx; +} @@ -273,7 +273,7 @@ </packing> </child> <child> - <object class="GtkLabel"> + <object class="GtkLabel" id="data-type-label"> <property name="visible">True</property> <property name="can-focus">False</property> <property name="tooltip-text" translatable="yes">How to interpret the process' memory.</property> @@ -287,226 +287,587 @@ </packing> </child> <child> - <object class="GtkRadioButton" id="type-u8"> - <property name="label" translatable="yes">8-bit unsigned integer</property> - <property name="name">u8</property> + <object class="GtkBox" id="data-type-box"> <property name="visible">True</property> - <property name="can-focus">True</property> - <property name="receives-default">False</property> - <property name="active">True</property> - <property name="draw-indicator">True</property> - <signal name="toggled" handler="update_configuration" swapped="no"/> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">3</property> - </packing> - </child> - <child> - <object class="GtkRadioButton" id="type-s8"> - <property name="label" translatable="yes">8-bit signed integer</property> - <property name="name">s8</property> - <property name="visible">True</property> - <property name="can-focus">True</property> - <property name="receives-default">False</property> - <property name="active">True</property> - <property name="draw-indicator">True</property> - <property name="group">type-u8</property> - <signal name="toggled" handler="update_configuration" swapped="no"/> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">4</property> - </packing> - </child> - <child> - <object class="GtkRadioButton" id="type-u16"> - <property name="label" translatable="yes">16-bit unsigned integer</property> - <property name="name">u16</property> - <property name="visible">True</property> - <property name="can-focus">True</property> - <property name="receives-default">False</property> - <property name="active">True</property> - <property name="draw-indicator">True</property> - <property name="group">type-u8</property> - <signal name="toggled" handler="update_configuration" swapped="no"/> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">5</property> - </packing> - </child> - <child> - <object class="GtkRadioButton" id="type-s16"> - <property name="label" translatable="yes">16-bit signed integer</property> - <property name="name">s16</property> - <property name="visible">True</property> - <property name="can-focus">True</property> - <property name="receives-default">False</property> - <property name="draw-indicator">True</property> - <property name="group">type-u8</property> - <signal name="toggled" handler="update_configuration" swapped="no"/> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">6</property> - </packing> - </child> - <child> - <object class="GtkRadioButton" id="type-u32"> - <property name="label" translatable="yes">32-bit unsigned integer</property> - <property name="name">u32</property> - <property name="visible">True</property> - <property name="can-focus">True</property> - <property name="receives-default">False</property> - <property name="draw-indicator">True</property> - <property name="group">type-u8</property> - <signal name="toggled" handler="update_configuration" swapped="no"/> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">7</property> - </packing> - </child> - <child> - <object class="GtkRadioButton" id="type-s32"> - <property name="label" translatable="yes">32-bit signed integer</property> - <property name="name">s32</property> - <property name="visible">True</property> - <property name="can-focus">True</property> - <property name="receives-default">False</property> - <property name="draw-indicator">True</property> - <property name="group">type-u8</property> - <signal name="toggled" handler="update_configuration" swapped="no"/> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">8</property> - </packing> - </child> - <child> - <object class="GtkRadioButton" id="type-u64"> - <property name="label" translatable="yes">64-bit unsigned integer</property> - <property name="name">u64</property> - <property name="visible">True</property> - <property name="can-focus">True</property> - <property name="receives-default">False</property> - <property name="draw-indicator">True</property> - <property name="group">type-u8</property> - <signal name="toggled" handler="update_configuration" swapped="no"/> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">9</property> - </packing> - </child> - <child> - <object class="GtkRadioButton" id="type-s64"> - <property name="label" translatable="yes">64-bit signed integer</property> - <property name="name">s64</property> - <property name="visible">True</property> - <property name="can-focus">True</property> - <property name="receives-default">False</property> - <property name="draw-indicator">True</property> - <property name="group">type-u8</property> - <signal name="toggled" handler="update_configuration" swapped="no"/> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">10</property> - </packing> - </child> - <child> - <object class="GtkRadioButton" id="type-ascii"> - <property name="label" translatable="yes">ASCII text</property> - <property name="name">ascii</property> - <property name="visible">True</property> - <property name="can-focus">True</property> - <property name="receives-default">False</property> - <property name="draw-indicator">True</property> - <property name="group">type-u8</property> - <signal name="toggled" handler="update_configuration" swapped="no"/> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">11</property> - </packing> - </child> - <child> - <object class="GtkRadioButton" id="type-utf16"> - <property name="label" translatable="yes">UTF-16 text</property> - <property name="name">utf16</property> - <property name="visible">True</property> - <property name="can-focus">True</property> - <property name="receives-default">False</property> - <property name="active">True</property> - <property name="draw-indicator">True</property> - <property name="group">type-u8</property> - <signal name="toggled" handler="update_configuration" swapped="no"/> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">12</property> - </packing> - </child> - <child> - <object class="GtkRadioButton" id="type-utf32"> - <property name="label" translatable="yes">UTF-32 text</property> - <property name="name">utf32</property> - <property name="visible">True</property> - <property name="can-focus">True</property> - <property name="receives-default">False</property> - <property name="draw-indicator">True</property> - <property name="group">type-u8</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">13</property> - </packing> - </child> - <child> - <object class="GtkRadioButton" id="type-f32"> - <property name="label" translatable="yes">32-bit floating-point</property> - <property name="name">f32</property> - <property name="visible">True</property> - <property name="can-focus">True</property> - <property name="receives-default">False</property> - <property name="draw-indicator">True</property> - <property name="group">type-u8</property> - <signal name="toggled" handler="update_configuration" swapped="no"/> + <property name="can-focus">False</property> + <property name="orientation">vertical</property> + <child> + <object class="GtkRadioButton" id="type-u8"> + <property name="label" translatable="yes">8-bit unsigned integer</property> + <property name="name">u8</property> + <property name="visible">True</property> + <property name="can-focus">True</property> + <property name="receives-default">False</property> + <property name="active">True</property> + <property name="draw-indicator">True</property> + <signal name="toggled" handler="update_configuration" swapped="no"/> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkRadioButton" id="type-s8"> + <property name="label" translatable="yes">8-bit signed integer</property> + <property name="name">s8</property> + <property name="visible">True</property> + <property name="can-focus">True</property> + <property name="receives-default">False</property> + <property name="active">True</property> + <property name="draw-indicator">True</property> + <property name="group">type-u8</property> + <signal name="toggled" handler="update_configuration" swapped="no"/> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">1</property> + </packing> + </child> + <child> + <object class="GtkRadioButton" id="type-u16"> + <property name="label" translatable="yes">16-bit unsigned integer</property> + <property name="name">u16</property> + <property name="visible">True</property> + <property name="can-focus">True</property> + <property name="receives-default">False</property> + <property name="active">True</property> + <property name="draw-indicator">True</property> + <property name="group">type-u8</property> + <signal name="toggled" handler="update_configuration" swapped="no"/> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">2</property> + </packing> + </child> + <child> + <object class="GtkRadioButton" id="type-s16"> + <property name="label" translatable="yes">16-bit signed integer</property> + <property name="name">s16</property> + <property name="visible">True</property> + <property name="can-focus">True</property> + <property name="receives-default">False</property> + <property name="draw-indicator">True</property> + <property name="group">type-u8</property> + <signal name="toggled" handler="update_configuration" swapped="no"/> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">3</property> + </packing> + </child> + <child> + <object class="GtkRadioButton" id="type-u32"> + <property name="label" translatable="yes">32-bit unsigned integer</property> + <property name="name">u32</property> + <property name="visible">True</property> + <property name="can-focus">True</property> + <property name="receives-default">False</property> + <property name="draw-indicator">True</property> + <property name="group">type-u8</property> + <signal name="toggled" handler="update_configuration" swapped="no"/> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">4</property> + </packing> + </child> + <child> + <object class="GtkRadioButton" id="type-s32"> + <property name="label" translatable="yes">32-bit signed integer</property> + <property name="name">s32</property> + <property name="visible">True</property> + <property name="can-focus">True</property> + <property name="receives-default">False</property> + <property name="draw-indicator">True</property> + <property name="group">type-u8</property> + <signal name="toggled" handler="update_configuration" swapped="no"/> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">5</property> + </packing> + </child> + <child> + <object class="GtkRadioButton" id="type-u64"> + <property name="label" translatable="yes">64-bit unsigned integer</property> + <property name="name">u64</property> + <property name="visible">True</property> + <property name="can-focus">True</property> + <property name="receives-default">False</property> + <property name="draw-indicator">True</property> + <property name="group">type-u8</property> + <signal name="toggled" handler="update_configuration" swapped="no"/> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">6</property> + </packing> + </child> + <child> + <object class="GtkRadioButton" id="type-s64"> + <property name="label" translatable="yes">64-bit signed integer</property> + <property name="name">s64</property> + <property name="visible">True</property> + <property name="can-focus">True</property> + <property name="receives-default">False</property> + <property name="draw-indicator">True</property> + <property name="group">type-u8</property> + <signal name="toggled" handler="update_configuration" swapped="no"/> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">7</property> + </packing> + </child> + <child> + <object class="GtkRadioButton" id="type-f32"> + <property name="label" translatable="yes">32-bit floating-point</property> + <property name="name">f32</property> + <property name="visible">True</property> + <property name="can-focus">True</property> + <property name="receives-default">False</property> + <property name="draw-indicator">True</property> + <property name="group">type-u8</property> + <signal name="toggled" handler="update_configuration" swapped="no"/> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">8</property> + </packing> + </child> + <child> + <object class="GtkRadioButton" id="type-f64"> + <property name="label" translatable="yes">64-bit floating-point</property> + <property name="name">f64</property> + <property name="visible">True</property> + <property name="can-focus">True</property> + <property name="receives-default">False</property> + <property name="draw-indicator">True</property> + <property name="group">type-u8</property> + <signal name="toggled" handler="update_configuration" swapped="no"/> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">9</property> + </packing> + </child> + <child> + <object class="GtkRadioButton" id="type-ascii"> + <property name="label" translatable="yes">ASCII text</property> + <property name="name">ascii</property> + <property name="visible">True</property> + <property name="can-focus">True</property> + <property name="receives-default">False</property> + <property name="draw-indicator">True</property> + <property name="group">type-u8</property> + <signal name="toggled" handler="update_configuration" swapped="no"/> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">10</property> + </packing> + </child> + <child> + <object class="GtkRadioButton" id="type-utf16"> + <property name="label" translatable="yes">UTF-16 text</property> + <property name="name">utf16</property> + <property name="visible">True</property> + <property name="can-focus">True</property> + <property name="receives-default">False</property> + <property name="active">True</property> + <property name="draw-indicator">True</property> + <property name="group">type-u8</property> + <signal name="toggled" handler="update_configuration" swapped="no"/> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">11</property> + </packing> + </child> + <child> + <object class="GtkRadioButton" id="type-utf32"> + <property name="label" translatable="yes">UTF-32 text</property> + <property name="name">utf32</property> + <property name="visible">True</property> + <property name="can-focus">True</property> + <property name="receives-default">False</property> + <property name="draw-indicator">True</property> + <property name="group">type-u8</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">12</property> + </packing> + </child> </object> <packing> <property name="expand">False</property> <property name="fill">True</property> - <property name="position">14</property> + <property name="position">16</property> </packing> </child> <child> - <object class="GtkRadioButton" id="type-f64"> - <property name="label" translatable="yes">64-bit floating-point</property> - <property name="name">f64</property> + <object class="GtkBox" id="search-box"> <property name="visible">True</property> - <property name="can-focus">True</property> - <property name="receives-default">False</property> - <property name="draw-indicator">True</property> - <property name="group">type-u8</property> - <signal name="toggled" handler="update_configuration" swapped="no"/> + <property name="can-focus">False</property> + <property name="orientation">vertical</property> + <child> + <object class="GtkLabel" id="search-label"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="label" translatable="yes">Search for a value</property> + <attributes> + <attribute name="weight" value="bold"/> + </attributes> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkBox" id="pre-search"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="orientation">vertical</property> + <child> + <object class="GtkLabel" id="search-type-label"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="label" translatable="yes">Search type:</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkRadioButton" id="enter-value"> + <property name="label" translatable="yes">Enter a value</property> + <property name="name">enter-value</property> + <property name="visible">True</property> + <property name="can-focus">True</property> + <property name="receives-default">False</property> + <property name="tooltip-text" translatable="yes">At every step in the search, enter the current value of what you're looking for (or an approximate value for floating-point numbers).</property> + <property name="active">True</property> + <property name="draw-indicator">True</property> + <signal name="toggled" handler="update_configuration" swapped="no"/> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">1</property> + </packing> + </child> + <child> + <object class="GtkRadioButton" id="same-different"> + <property name="label" translatable="yes">Same/different</property> + <property name="name">same-different</property> + <property name="visible">True</property> + <property name="can-focus">True</property> + <property name="receives-default">False</property> + <property name="tooltip-text" translatable="yes">At every step in the search, enter whether or not the value has changed since the last step. This will use quite a bit of disk space, to record all the process' memory.</property> + <property name="active">True</property> + <property name="draw-indicator">True</property> + <property name="group">enter-value</property> + <signal name="toggled" handler="update_configuration" swapped="no"/> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">2</property> + </packing> + </child> + <child> + <object class="GtkBox" id="required-memory-box"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <child> + <object class="GtkLabel" id="required-memory-label"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="label" translatable="yes">Memory required (approx): </property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="required-memory"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="label" translatable="yes">N/A</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">1</property> + </packing> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">3</property> + </packing> + </child> + <child> + <object class="GtkBox" id="required-disk-box"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <child> + <object class="GtkLabel" id="required-disk-label"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="label" translatable="yes">Disk space required (approx): </property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="required-disk"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="label" translatable="yes">N/A</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">1</property> + </packing> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">4</property> + </packing> + </child> + <child> + <object class="GtkButton" id="begin-search"> + <property name="label" translatable="yes">Begin search</property> + <property name="visible">True</property> + <property name="can-focus">True</property> + <property name="receives-default">True</property> + <property name="tooltip-text" translatable="yes">Start searching for a value in memory. Make sure the value is stored somewhere in memory before you start.</property> + <signal name="clicked" handler="search_start" swapped="no"/> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">5</property> + </packing> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">1</property> + </packing> + </child> + <child> + <object class="GtkBox" id="search-enter-value"> + <property name="can-focus">False</property> + <property name="no-show-all">True</property> + <child> + <object class="GtkLabel" id="current-value-label"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="label" translatable="yes">Current value:</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkEntry" id="current-value"> + <property name="visible">True</property> + <property name="can-focus">True</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">1</property> + </packing> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">2</property> + </packing> + </child> + <child> + <object class="GtkBox" id="search-same-different"> + <property name="can-focus">False</property> + <property name="no-show-all">True</property> + <child> + <object class="GtkRadioButton" id="same"> + <property name="label" translatable="yes">Same</property> + <property name="name">same</property> + <property name="visible">True</property> + <property name="can-focus">True</property> + <property name="receives-default">False</property> + <property name="margin-end">5</property> + <property name="active">True</property> + <property name="draw-indicator">True</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkRadioButton" id="different"> + <property name="label" translatable="yes">Different</property> + <property name="name">different</property> + <property name="visible">True</property> + <property name="can-focus">True</property> + <property name="receives-default">False</property> + <property name="active">True</property> + <property name="draw-indicator">True</property> + <property name="group">same</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">1</property> + </packing> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">3</property> + </packing> + </child> + <child> + <object class="GtkBox" id="search-common"> + <property name="can-focus">False</property> + <property name="no-show-all">True</property> + <property name="orientation">vertical</property> + <child> + <object class="GtkBox" id="steps-completed-box"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <child> + <object class="GtkLabel" id="steps-completed"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="label" translatable="yes">0</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="steps-completed-label"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="label" translatable="yes"> steps completed</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">1</property> + </packing> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkBox" id="candidates-left-box"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="tooltip-text" translatable="yes">Number of possible places where this value could be</property> + <child> + <object class="GtkLabel" id="candidates-left"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="label" translatable="yes">0</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="candidates-left-label"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="label" translatable="yes"> candidates left</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">1</property> + </packing> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">1</property> + </packing> + </child> + <child> + <object class="GtkButton" id="same-different-update"> + <property name="label" translatable="yes">Update</property> + <property name="visible">True</property> + <property name="can-focus">True</property> + <property name="receives-default">True</property> + <property name="tooltip-text" translatable="yes">Click this to indicate that the value in the box above is the current value of the thing you're looking for.</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">2</property> + </packing> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">4</property> + </packing> + </child> </object> <packing> <property name="expand">False</property> <property name="fill">True</property> - <property name="position">15</property> + <property name="position">18</property> </packing> </child> </object> @@ -1,3 +1,4 @@ +// Functions for handling unicode text. #ifndef UNICODE_H_ #define UNICODE_H_ #define UNICODE_BOX_CHARACTER 0x2610 |