UNIVERSIDAD DE BUENOS AIRES FACULTAD DE INGENIERÍA C ARRERA DE E SPECIALIZACIÓN EN S ISTEMAS E MBEBIDOS M EMORIA DEL T RABAJO F INAL Herramienta Gráfica para Analizar la Ocupación de Memoria en Sistemas Embebidos Autor: Ing. Alejandro C ELERY Director: Ing. Juan Manuel C RUZ Jurados: Ing. Juan A LARCÓN Ms. Ing. Roberto B ARNEDA Ing. Gerardo S AGER Este trabajo fue realizado en las Ciudad Autónoma de Buenos Aires, entre agosto de 2015 y julio de 2016. III Resumen En el presente trabajo se plantea el uso de treemaps o gráficos de mapa de árbol para representar gráficamente como se ocupan las memorias de código y datos de un microcontrolador. Se desarrolla un plugin para Eclipse que dibuja un treemap y presenta una lista de los nombres encontrados en la tabla de símbolos de un archivo binario. V Agradecimientos En primer lugar a mi pareja, Esmeralda Yañez Illera. Solo ella sabe los sacrificios que ha hecho para permitirme hacer esta especialización. A mi padre, por haberme permitido llegar hasta donde estoy hoy, por los valores que me transmitió por el camino, y por el orgullo que siente en este momento desde donde sea que me esté viendo. A la comunidad del software libre en general, por haberme brindado las herramientas con las que ejercí la profesión hasta hoy (y a la que espero poder devolverle algo con este trabajo). A las autoridades de la CESE por haberme otorgado una beca para hacer este curso, sin la cual no hubiera sido posible mi presencia. A Juan Manuel Cruz, siempre presente, tutor de este trabajo puntual y de muchas otras cosas más. A Roberto Barneda, por la inmensurable cantidad de sabiduría que derramó sobre mi persona durante mis años formativos. Ojalá el destino me permita hacer los mismo por las generaciones futuras. A Rubén Lozano y Juan Alarcón, compañeros en la profesión, por apoyarme en esta empresa siendo mis jurados. Espero que mi trabajo les compense de alguna manera el tiempo que invirtieron en acompañarme. A Ben Shneiderman, creador de los diagramas NS y de los treemaps, por compartir su investigación conmigo y por interesarse en mi trabajo. Al Ingeniero Mirko Serra, incansable compañero con quien compartimos el último y el más difícil tramo de la carrera académica. Aún no se dónde estaría sin la exitosa sociedad que supimos formar y que espero continúe durante muchos años. VII Índice general Resumen III Agradecimientos V Registro de versiones XI 1. Introducción General 1.1. Problemática de los consumos de memoria en un microcontrolador . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.2. Presentación de los treemaps . . . . . . . . . . . . . . . . . . 1.3. Presentación de Eclipse y su uso de plugins . . . . . . . . . . 1.4. Solución propuesta . . . . . . . . . . . . . . . . . . . . . . . . 1 2. Introducción Específica 2.1. Tabla de símbolos generada por el compilador . . . . . . . . 2.2. Herramientas disponibles de análisis de la tabla de símbolos 2.2.1. Herramienta objdump . . . . . . . . . . . . . . . . . . 2.2.2. Herramienta nm . . . . . . . . . . . . . . . . . . . . . 2.2.3. Herramienta pahole . . . . . . . . . . . . . . . . . . . 2.3. Investigacion sobre treemaps . . . . . . . . . . . . . . . . . . 2.4. Diseño y desarrollo de un plugin . . . . . . . . . . . . . . . . 7 7 8 9 9 10 12 13 3. Diseño e Implementación 3.1. Análisis de la tabla de símbolos . . . . . . . . . . . . 3.2. Desarrollo del plugin . . . . . . . . . . . . . . . . . . 3.2.1. Lista completa de símbolos . . . . . . . . . . 3.2.2. Lista filtrada de símbolos . . . . . . . . . . . 3.2.3. Treemap . . . . . . . . . . . . . . . . . . . . . 3.2.4. Interacciones entre las views . . . . . . . . . 3.2.5. Obtención de la tabla de símbolos a graficar 3.2.6. Modelo de los datos en memoria . . . . . . . 3.2.7. Presentación de los datos al usuario . . . . . 3.2.8. Opciones de configuración del plugin . . . . 3.2.9. Depuración del plugin . . . . . . . . . . . . . 3.3. Adopción de un algoritmo de treemapping . . . . . 3.3.1. Resolución gráfica . . . . . . . . . . . . . . . 3.3.2. Relación de aspecto . . . . . . . . . . . . . . . 3.3.3. Preservación del orden de los datos . . . . . 3.3.4. Estabilidad de los datos . . . . . . . . . . . . 3.3.5. Algoritmo elegido . . . . . . . . . . . . . . . 15 15 16 18 18 19 20 21 21 21 21 23 24 24 25 25 26 26 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 2 3 3 VIII 4. Ensayos y Resultados 4.1. Precisión del análisis de la tabla de símbolos . . . . . . . . . 4.1.1. Precisión de la identificación de secciones de la herramienta nm . . . . . . . . . . . . . . . . . . . . . . . . . 4.1.2. Resultados del ensayo . . . . . . . . . . . . . . . . . . 4.2. Consumo de recursos de hardware causado por el plugin . . 4.2.1. Medición del tiempo de procesamiento . . . . . . . . 4.2.2. Resultados del ensayo . . . . . . . . . . . . . . . . . . 4.3. Validación con usuarios finales . . . . . . . . . . . . . . . . . 4.3.1. Esp. Ing. Pablo Ridolfi . . . . . . . . . . . . . . . . . . 4.3.2. Ing. Mirko Serra . . . . . . . . . . . . . . . . . . . . . 5. Conclusiones 5.1. Grado de cumplimiento de los requerimientos del proyecto . 5.1.1. Representación visual del uso de la memoria interna del MCU . . . . . . . . . . . . . . . . . . . . . . . . . . 5.1.2. Fácil identificación de los principales consumos de memoria . . . . . . . . . . . . . . . . . . . . . . . . . . 5.1.3. Indicación de a qué zona de memoria fue asignada cada variable . . . . . . . . . . . . . . . . . . . . . . . 5.1.4. Presentación de estos datos dentro de una ventana de Eclipse . . . . . . . . . . . . . . . . . . . . . . . . . . . 5.2. Próximos pasos . . . . . . . . . . . . . . . . . . . . . . . . . . 29 29 29 30 32 32 33 33 34 35 37 37 37 38 38 39 39 A. Programa usado para los ensayos de medición 41 Bibliografía 43 IX Índice de figuras 1.1. 1.2. 1.3. 1.4. Vista de la aplicación Windirstat . . . . . . . . Eclipse dibujando gráficos 2D . . . . . . . . . . Eclipse dibujando gráficos 3D . . . . . . . . . . Eclipse usado para diseño basado en modelos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.1. 2.2. 2.3. 2.4. 2.5. 2.6. 2.7. Fragmento de un archivo “.map”, sección “.bss” . . . . . . . Fragmento de un archivo “.map”, sección “.text” . . . . . . . Fragmento de la salida del comando objdump . . . . . . . . Fragmento de la salida del comando nm . . . . . . . . . . . . Fragmento de la salida del comando pahole . . . . . . . . . . Vista del programa TreeViz . . . . . . . . . . . . . . . . . . . Detalle de los componentes de la interfaz de usuario de Eclipse 2 4 4 5 7 8 9 10 11 12 14 3.1. Código de la clase “Shell”. . . . . . . . . . . . . . . . . . . . . 3.2. Códigos con los que el comando nm identifica cada sección de memoria. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3.3. Código de la clase “Symbol”. . . . . . . . . . . . . . . . . . . 3.4. Lista completa de símbolos generada por el plugin . . . . . . 3.5. Lista filtrada de símbolos generada por el plugin . . . . . . . 3.6. Treemap generado por el plugin . . . . . . . . . . . . . . . . 3.7. Fragmentos del código que genera la lista completa de símbolos del plugin. . . . . . . . . . . . . . . . . . . . . . . . . . . 3.8. Diálogo de opciones de configuración . . . . . . . . . . . . . 3.9. Treemaps con alta relación de aspecto . . . . . . . . . . . . . 3.10. Esquema del algoritmo “Squarify” . . . . . . . . . . . . . . . 15 4.1. Imagen de la salida del size al analizar el programa de prueba. 4.2. Vista de la sumatoria del tamaño de los símbolos en la sección .bss . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4.3. Vista de la sumatoria del tamaño de los símbolos en la sección .data . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4.4. Vista de la sumatoria del tamaño de los símbolos en la sección .text . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30 5.1. Resultado final del desarrollo . . . . . . . . . . . . . . . . . . 37 A.1. Programa usado para contrastar los reportes del plugin contra el comando size. . . . . . . . . . . . . . . . . . . . . . . . . 42 16 17 18 19 20 22 23 25 27 31 31 32 XI Registro de versiones Revisión 1.0 1.1 1.2 1.3 Cambios realizados Creación del documento Correcciones de formato Se agrega el capítulo “Ensayos y resultados” Se agregan reportes de uso a la sección “Validacion con usuarios finales.” Fecha 27/06/2016 29/06/2016 10/07/2016 15/07/2016 1 Capítulo 1 Introducción General 1.1. Problemática de los consumos de memoria en un microcontrolador Los microcontroladores son dispositivos electrónicos omnipresentes en el mundo de hoy. Están formados por un microprocesador capaz de ejecutar un programa almacenado previamente en su memoria interna y por un conjunto de periféricos de comunicación e interfaces eléctricas que le permiten interactuar con el mundo exterior. Su costo ha bajado mucho en los últimos años, causando que en la gran mayoría de los casos sean la solución electrónica que mejor se adapta a cualquier requerimiento de un dispositivo de control. La memoria interna en la que almacenan el programa a ejecutar y los datos que este utiliza tiene una capacidad finita, por lo que debe observarse cómo se emplea la misma. El dato de cómo se consume la memoria en una aplicación con microcontrolador era de uso cotidiano en la época de los microcontroladores con unos pocos cientos de bytes de memoria de datos pero se fue perdiendo con el advenimiento de dispositivos con cada vez más capacidad de memoria (actualmente hay microcontroladores con 1Mb de memoria de código y 192Kb de memoria de datos, contra los 8Kb y 256 bytes del primer microcontrolador con el que el autor tuvo su bautismo de fuego en la profesión). La infromación del consumo de memoria la emite el compilador cada vez que compila la aplicación, pero lo hace en un formato cuasi críptico que es de difícil asimilación por programadores no experimentados. Aún en el caso de programadores con trayectoria probada, buscar la información en ese formato conlleva un gasto improductivo de tiempo. La experiencia ha demostrado que se presentan dos situaciones cotidianas en el desarrollo de sistemas embebidos en las que este consumo se debe tener muy presente: Al encarar un desarrollo nuevo, se debe reducir al mínimo posible la memoria utilizada por la aplicación con el fin de elegir el microcontrolador más económico que la pueda contener. Al ampliar un diseño existente, se puede agotar la memoria disponible y ser necesario agregar una nueva funcionalidad. Ante esta problemática, las herramientas de compilación gratuitas disponibles hoy en día al programador no ofrecen al mismo una solución. Si bien existe la posibilidad de que las herramientas de uso profesional sí lo hagan, esta posibilidad no se ha estudidado por el costo prohibitivo que las mismas exhiben. 2 Capítulo 1. Introducción General 1.2. Presentación de los treemaps Los treemaps (o mapas de árbol) son un tipo de diagrama muy útil que puede ayudar con la problemática presentada anteriormente. Siendo una herramienta 100 % visual, se prefiere mostrarla antes que intentar describirla. Para contextualizar, la figura 1.1 muestra una imagen de una aplicación llamada WinDirStat [18] que sirve para analizar cómo se está ocupando el espacio disponible en un disco rígido, indicando cuáles archivos y/o carpetas ocupan el mayor espacio. Figura 1.1: Vista de la aplicación Windirstat En la figura 1.1 se pueden apreciar varias cuestiones: En la ventana superior izquierda, una lista de las carpetas presentes en el disco rígido ordenadas de mayor a menor. En la parte derecha, una lista de los tipos de archivo encontrados (imágenes, música, documentos, etc.) con un color asociado a cada tipo. En la parte inferior, un mapa gráfico con numerosos rectángulos de distintos colores y tamaños. Cada archivo presente en el disco rígido está representado por un rectángulo, donde su tamaño relativo al total del espacio ocupado se aprecia en el tamaño del rectángulo (para ejemplificar, si un archivo ocupara la mitad del espacio en disco, su rectángulo ocuparía la mitad del área graficada) y el color corresponde al tipo de archivo indicado en la parte superior derecha. Cabe mencionar que para analizar un disco rígido no se lo recorre físicamente sector por sector sino que al comienzo del mismo hay una tabla llamada FAT (File Allocation Table), que contiene el índice de qué archivo está ubicado en qué parte del disco y qué tamaño ocupa. 1.3. Presentación de Eclipse y su uso de plugins 3 Recapitulando, existe una herramienta capaz de mostrar en forma gráfica e inmediata las relaciones de tamaño entre una gran cantidad de archivos identificados al analizar una tabla de ubicación de un disco rígido. El lector con experiencia en sistemas embebidos tal vez esté asociando en este momento esta herramienta con la problemática presentada en la sección 1.1, que fue lo que hiciera el autor al verla por primera vez. Se discute esta posibilidad en la sección 1.4. 1.3. Presentación de Eclipse y su uso de plugins La herramienta de uso más popularizado en el desarrollo de sistemas embebidos es sin duda un programa llamado Eclipse. Su historia se puede leer en Wikipedia [1], pero basta mencionar aquí su característica fundamental: es un programa libre y sumamente modular compuesto por un pequeño núcleo cuya única función es cargar módulos conteniendo la funcionalidad deseada llamados “plugins” o “enchufables”. De este modo, una única herramienta se puede utilizar para un sinnúmero de propósitos siempre y cuando tenga disponibles los plugins necesarios. La aplicación es 100 % libre y de código abierto, y están sumamente documentados su uso y las técnicas de programación requeridas para desarrollar nuevos plugins. Por este motivo se han escrito módulos para permitirle funcionar para muchos propósitos. Sin ir más lejos, el plugin objeto de este trabajo fue escrito en un entorno Eclipse y también lo fue este documento en su totalidad, en ambos casos con el agregado de los plugins correspondientes. En el desarrollo de sistemas embebidos se ha impuesto por sus numerosas virtudes, entre otras: Es una herramienta libre y sin costo alguno. Es muy sencillo de integrar con otras herramientas libres. Es sumamente personalizable Funciona indistintamente en Linux, Windows y MAC-OS. Permite un uso muy flexible del espacio disponible en la pantalla. Esto es muy útil en estos días de trabajar con computadores portátiles adosados a un segundo monitor o un proyector. Las siguientes tres figuras muestran sendas imágenes de Eclipse cumpliendo roles bien diferentes con el espíritu de transmitir su versatilidad. El único requisito para poder programar un plugin es hacerlo en el lenguaje Java, pero el mismo es lo bastante similar al lenguaje C++ y de alguna manera al lenguaje C (el de mayor uso en el desarrollo de SE) como para que un programador de SE lo bastante motivado pueda emprender el desarrollo de un plugin. 1.4. Solución propuesta De los expuesto anteriormente, se puede inferir que tomando los siguientes recaudos se puede obtener una herramienta de gran valor: 4 Capítulo 1. Introducción General Figura 1.2: Eclipse dibujando gráficos 2D Figura 1.3: Eclipse dibujando gráficos 3D En lugar de analizar una tabla de ubicación de archivos en un disco rígido, se debe analizar una tabla de ubicación de variables en la memoria de un microcontrolador. Se debe contemplar que las relaciones de anidamiento (es decir, un archivo dentro de una carpeta dentro de otra carpeta) no están presentes en la memoria de un microcontrolador (se discutirán más adelante algunas excepciones). 1.4. Solución propuesta 5 Figura 1.4: Eclipse usado para diseño basado en modelos Hay que obtener de alguna manera el algoritmo o receta para dibujar un mapa propio, producto del análisis anterior. Todo esto se discutirá en el capítulo 2. Habiendo visto la problemática de los consumos de memoria, las bondades de los gráficos de treemap para representarla y la posibilidad de ampliar la herramienta de trabajo de uso cotidiano para incluir estos diagramas, resulta interesante la posibilidad de desarrollar un plugin para Eclipse que atendiera esta problemática. Entonces, con la motivación de diseñar y desarrollar una herramienta de software que asista a los programadores de sistemas embebidos en su función, permitiéndoles ver rápidamente cómo se está empleando la memoria interna del microcontrolador en uso; con la esperanza de que esta herramienta ahorre tiempo al desarrollador al ejercer su función y le permita tomar mejores decisiones a la hora de elegir un microcontrolador para un proyecto nuevo es que se eligió esta temática para desarrollar el presente trabajo. Durante el curso de gestión de proyectos se planificó este trabajo, y se pusieron los siguientes requerimientos: 1. Representación visual del uso de la memoria interna de un microcontrolador. 2. Fácil identificación de los principales consumos de memoria. 3. Indicación de a qué zona de memoria fue asignada cada variable (FLASH, RAM, EEPROM, etc). 4. Presentación de estos datos dentro de una ventana de Eclipse. Se definió el alcance del proyecto como “la realización de dicho plugin integrable a Eclipse y la entrega de un manual de usuario que permita la 6 Capítulo 1. Introducción General instalación y uso del mismo. En este proyecto no se contemplan cursos de capacitación en el uso de la herramienta generada.” Fundamentado en los siguientes supuestos: 1. No hay actualmente una herramienta que cumpla estos requisitos. 2. Hay una población de programadores de sistemas embebidos que se beneficiaría de tener tal herramienta. 3. El programador no experimentado suele ignorar esta problemática y tal herramienta lo ayudará a superar la misma. Habiendo expuesto el panorama encontrado al momento de planificar el proyecto y los supuestos sobre los que se decidió llevarlo adelante, se pasa a la siguiente sección en la que se describen en detalle los componentes que formaron parte de la solución y sus problemáticas asociadas. 7 Capítulo 2 Introducción Específica 2.1. Tabla de símbolos generada por el compilador El punto de partida del análisis detallado tiene que ser necesariamente la identificación de los datos a graficar. El primer contacto con ellos es un archivo llamado “map file” o mapa del archivo, que el compilador genera luego de hacer una compilación. La figura 2.1 presenta un fragmento del mismo para referencia del lector: .bss.bufferColumnaVacia 0x10002b88 0x7f ...(CM3_Display_C12.o) 0x10002b88 bufferColumnaVacia .bss.bufferColumnaGrafica 0x10002c07 0x7f ...(CM3_Display_C12.o) 0x10002c07 bufferColumnaGrafica .bss.buffer10medicionesPong 0x10002c86 0xa ...(CM3_Display_C12.o) 0x10002c86 buffer10medicionesPong .bss._ZZ16BSP_DibujarPuntoiimbE14tocaTransferir 0x10002c90 0x1 ...(CM3_Display_C12.o) 0x3 *fill* 0x10002c91 Figura 2.1: Fragmento de un archivo “.map”, sección “.bss” El programador experimentado puede inferir rápidamente la siguiente información: Todos los símbolos están ubicados en una misma sección llamada “bss”. Los símbolos identificados son: bufferColumnaVacia, bufferColumnaGrafica, buffer10medicionesPong, tocaTransferir. El símbolo tocaTransferir es declarado con el modificador “static” y es local a la función BSP_DibujarPunto Las direcciones de memoria comienzan en 0x10002b88 Los tamaños ocupados por los símbolos son de 127, 127, 10 y 1 bytes (en el orden en que se reconocen los símbolos). El último símbolo de la lista ocupa 1 byte pero luego de él se reserva un espacio desperdiciado de 3 bytes, para que todos los símbolos empiecen en una dirección múltiplo de 4. 8 Capítulo 2. Introducción Específica Todos pertenecen al archivo CM3_Display_C12 Por la propia experiencia del autor, se concluye que estas determinaciones escapan al programador principiante. Ciertamente durante la formación académica recibida en la carrera de Ingeniería Electrónica (que generalmente son quienes suelen dedicarse a los sistemas embebidos) no se llega a examinar el software producido con el nivel de detalle suficiente para percibir estas cuestiones. En el fragmento analizado se hizo incapié en unas declaraciones de variables en el segmento de datos. Si ahora miramos un fragmento del mismo archivo referido al segmento de código y la declaración de funciones vemos que la situación es similar, como se puede ver en la figura 2.2: .text.ColaReset 0x00016db4 0x00016db4 .text.ColaInit 0x00016dce 0x00016dce .text.ColaCantidad 0x00016df2 0x00016df2 .text.ColaDisponible 0x00016e04 0x00016e04 0x1a ...(ColaCircular.o) ColaReset 0x24 ...(ColaCircular.o) ColaInit 0x12 ...(ColaCircular.o) ColaCantidad 0x16 ...(ColaCircular.o) ColaDisponible Figura 2.2: Fragmento de un archivo “.map”, sección “.text” En este caso se pueden sacar similares conclusiones, con la salvedad de que los símbolos están en la sección “text”. Para contextulizar finalmente la complejidad de encontrar estos datos en el archivo, vale mencionar que el ejemplo del que se tomaron estos fragmentos tiene 7230 líneas de largo y que el mismo incluye numerosos artefactos producidos por el compilador durante el proceso de compilación que no tienen importancia para el programador. 2.2. Herramientas disponibles de análisis de la tabla de símbolos Habiendo visto las vicisitudes de analizar manualmente el resultado del proceso de compilación, vale mencionar que existen herramientas de software que hacen ese trabajo notablemente más fácil. Dos de ellas son parte del grupo de herramientas llamadas “binutils” y están disponibles a partir de que se instalan las herramientas para programar un microcontrolador (en adelante “toolchain” o “cadena de herramientas”). Es importante recordar que la salida producida por estas herramientas solo sirve si puede ser recogida y tabulada para su posterior uso por herramientas gráficas, este fue el criterio que se utilizó para elegir una de las tres. 2.2. Herramientas disponibles de análisis de la tabla de símbolos <1><894a>: Número de Abrev: 143 <894c> DW_AT_decl_line : <894e> DW_AT_decl_file : <8950> DW_AT_type : <8954> DW_AT_external : <8955> DW_AT_location : <895b> DW_AT_name : <1><8965>: Número de Abrev: 130 <8967> DW_AT_type : <896b> DW_AT_external : <896c> DW_AT_declaration : <896d> DW_AT_name : <1><8998>: Número de Abrev: 130 <899a> DW_AT_type : <899e> DW_AT_external : <899f> DW_AT_declaration : <89a0> DW_AT_name : 9 (DW_TAG_variable) 835 50 <0x8aeb> 1 bloque de byte 5: 3 ... buffer_tx (DW_TAG_variable) <0x8b28> 1 1 c (DW_TAG_variable) <0x89c9> 1 1 hayContraseniaCorrecta Figura 2.3: Fragmento de la salida del comando objdump 2.2.1. Herramienta objdump Objdump fue la primera herramienta analizada. Es la más conocida de las tres por los programadores, ya que forma parte del proceso estándar de compiación de un programa para microcontrolador. El siguiente es un fragmento de su documentación disponible en forma online: “objdump muestra información sobre uno o más archivos objeto. Tiene opciones para controlar qué información en particular se muestra. Esta información es mayormente útiles para los programadores que trabajan en las herramientas de compilación y no para los programadores que solo quieren que su programa compile y funcione. . . ” [10]. Es una herramienta sumamente versátil y como tal tiene muchas opciones para configurar su uso. La dificultad en su uso se menciona en la descripción precedente: al estar orientada a desarrolladores de herramientas no hay ningún interés en presentar la información en forma sencilla. La figura 2.3 muestra un ejemplo de lo que se puede esperar cuando se la utiliza: Se puede ver que por cada entidad encontrada en el archivo analizado se produce un bloque que empieza con la línea que contiene “Número de Abrev”. A simple vista no se puede extraer la información de tamaño de los símbolos encontrados ni de la sección en la que fueron ubicados. Más aún, esta información probablemente esté en otra parte de la salida y sea necesario hacer una correlación de más de una. Habiendo visto la dificultad de encontrar la información buscada y más aún recogerla programáticamente para procesarla, se detuvo el análisis preliminar de esta herramienta en este punto y se pasó a la siguiente. 2.2.2. Herramienta nm Nm es una herramienta más sencilla, con un propósito más específico: “nm lista los símbolos encontrados en una lista de archivos objeto. . . ” [9]. 10 Capítulo 2. Introducción Específica 00049418 00070334 00032192 ... 08388612 08388622 ... 08388804 08389492 08389540 00000148 t _ProcEstadoInterfazBloqueada 00000148 t _ControlaPresionSeteada 00000142 t _VerificaFuentesGases 00000010 D _umbralEstadosBateriaDescarga 00000010 D _umbralEstadosBateriaCarga 00000032 B _flags_t_svc 00000032 B _flags_t_est 00000016 B _estado_equipo Figura 2.4: Fragmento de la salida del comando nm Por su descripción, nm parece ser la herramienta hecha a medida para el propósito buscado. La figura 2.4 muestra un ejemplo de su uso para compararlo con la anterior: Se puede ver a simple vista el formato de lista en el que se muestran los resultados. En una segunda inspección, se aprecia la siguiente información: La primera columna contiene la dirección en memoria de cada símbolo. La segunda muestra el tamaño de los mismos. Luego sigue una letra que el observador experimentado puede reconocer como la sección en la que se ubica el símbolo. Recuérdese que recabar esta información es uno de los requerimientos del proyecto. A esta altura el autor pudo apreciar que esta herramienta supera con creces a la anterior. En la sección 4.1 se analizará el grado de precisión con el que se extrae la información de esta tabla para tener una métrica cuantitativa de los resultados de este trabajo. 2.2.3. Herramienta pahole Pahole es una herramienta complementaria para los objetivos de este trabajo. “pahole muestra la disposición de las estructuras de datos que se almacena en los símbolos de depuración. . . ” [11]. Su propósito no es identificar los símbolos declarados en un programa sino hacer un análisis de cómo el compilador organiza las estructuras de datos declaradas por el programador. Para contextualizar este análisis se menciona que muchas veces estas estructuras se usan cuando se escribe código para acceder a hardware, y en estos casos es necesario que el compilador respete la ubicación exacta de cada bit y byte declarado por el programador. En algunos casos los compiladores no hacen esto sino que agregan bits de relleno en las estructuras buscando un acceso más rápido a la memoria. Por completitud, se muestra un ejemplo de su uso en la figura 2.5 : 2.2. Herramientas disponibles de análisis de la tabla de símbolos 11 struct st_evento { unsigned char cod_evento; unsigned char detalle_evento; unsigned char dia; unsigned char mes; unsigned char anio; unsigned char hora; unsigned char minutos; unsigned char segundos; /* /* /* /* /* /* /* /* 0 1 2 3 4 5 6 7 1 1 1 1 1 1 1 1 */ */ */ */ */ */ */ */ /* size: 8, cachelines: 1, members: 8 */ /* last cacheline: 8 bytes */ }; struct st_tabla_estado { unsigned short estado_on_off; /* unsigned short boot_count; /* unsigned short estado_falla; /* unsigned short horas_uso_cpu; /* unsigned short horas_ultimo_service; /* unsigned char minutos_uso_cpu; /* unsigned char t_est_reservado; /* unsigned short ciclos_valvulas; /* unsigned short horas_uso_turbina; /* 0 2 4 6 8 10 11 12 14 2 2 2 2 2 1 1 2 2 */ */ */ */ */ */ */ */ */ /* size: 16, cachelines: 1, members: 9 */ /* last cacheline: 16 bytes */ }; Figura 2.5: Fragmento de la salida del comando pahole 12 2.3. Capítulo 2. Introducción Específica Investigacion sobre treemaps Habiendo establecido la conveniencia de los mapas de árbol para representar información, resulta apropiado indagar un poco más sobre ellos. Se puede leer un racconto muy detallado de su historia en [13] contado por el propio Ben Shneiderman de la Universidad de Maryland, Estados Unidos, creador de estos diagramas. El comienzo de su investigación lo explica así (traducido por el autor): “En 1990, en respuesta a la problemática común de un disco rígido lleno, me obsesioné con la idea de producir una visualización compacta de las estructuras de árbol de directorios. Dado que el disco rígido de 80 Megabytes usado en el laboratorio de Interacción hombre-computador era compartido por 14 usuarios, era muy difícil determinar cómo y dónde se usaba su capacidad. . . ” “Los diagramas con estructura de árbol y vinculados en los nodos se volvían muy grandes para ser útiles, así que exploré maneras de mostrar un árbol en un formato con espacio limitado. Rechacé las estrategias que dejaban espacios en blanco.(. . . ) Mostrar el tamaño de los archivos codificándolo por área parecía atractivo, pero las formas rectangulares, triangulares y circulares todas tenían problemas. Finalmente un día meditando sobre esto en el salón de la facultad, tuve el momento Ajá! de dividir la pantalla en rectángulos alternando las direcciones vertical y horizontal a medida que se atraviesan los distintos niveles.” En el marco de su investigación, Ben Shneiderman y Brian Johnson escribieron la primera implementación de un visor de ocupación de espacio en disco rígido y lo bautizaron TreeViz. La figura 2.6 muestra una imagen del mismo. Figura 2.6: Vista del programa TreeViz, primero en implementar la visualización mediante treemaps. Figura tomada de [13] Luego en la misma página menciona el software que se adoptó en este trabajo: “Para apoyar a investigadores y estudiantes, Ben Bederson y Martin 2.4. Diseño y desarrollo de un plugin 13 Wattenberg escribieron una librería de código abierto en Java 1.1 con 5 algoritmos de treemap. Son genéricos y debieran poder usarse sencillamente en cualquier aplicación. Cada una toma una lista de números y un rectángulo, y genera un conjunto de sub-rectángulos que son proporcionales en área a los números de entrada y particiona el espacio del rectángulo de entrada. Los 5 algoritmos implementados son: BinaryTree - Partially ordered, not very good aspect ratios, stable Ordered - Partially ordered, medium aspect ratios, medium stability SliceAndDice - Ordered, very bad aspect ratios, stable Squarified - Unordered, best aspect ratios, medium stability Strip - Ordered, medium aspect ratios, medium stability” En este punto vale aclarar que un “algoritmo de treemapping” se debe entender como un criterio para repartir el espacio disponible entre los distintos rectángulos. Shneiderman ordena estos 5 algoritmos según sus capacidades de orden, relación de aspecto y estabilidad. La eleccción de uno de estos algoritmos y las características mencionadas de los mismos se discutirá en la sección 3.3. Lo último que resulta interesante citar son funciones adicionales que se desarrollaron durante la investigación: “La implementación del estudiante de doctorado Brian Johnson agregó muchas otras funciones interesantes, como ser zooming, sonido (como una codificación redundante, por ejemplo los archivos más grandes tenían un sonido más grave), control de matiz y saturación, muchas variantes de bordes y control de etiquetado. . . ” . Algunas de estas funciones adicionales se tomaron como ideas para los próximos pasos posteriores a este trabajo, como se muestra en la sección 5.2. 2.4. Diseño y desarrollo de un plugin Se ha dicho anteriormente que Eclipse es una plataforma formada por un pequeño núcleo capaz de cargar módulos externos a él y que le brindan funcionalidad. A continuación analizamos estos módulos y cómo se desarrolla uno propio. Tanto el núcleo de Eclipse como los plugins se escriben en el lenguaje Java. De hecho, Eclipse nace dentro de la firma IBM [1] como un entorno para programar en dicho lenguaje y se lo considera el mejor entorno para tal función. Así las cosas, un plugin no es más que un pequeño programa en Java que se desarrolla usando el mismo Eclipse. Para simplificar el proceso de desarrollo, el mismo no se hace partiendo de un lienzo en blanco sino que se parte de diversas plantillas de plugins que luego el programador modifica para sus propósitos. Tanto estas plantillas como las herramientas de software necesarias para escribir, ensayar y validar un plugin están contenidas dentro del llamado PDE o “Plugin Development Environment”, es menester instalarlo para encarar el desarrollo de un plugin. Casualmente, el PDE es un conjunto de plugins diseñados para permitir el desarrollo de otros plugins. 14 Capítulo 2. Introducción Específica Los plugins tienen una característica particular: por un lado se listan en forma declarativa las capacidades que aportan a Eclipse y su funcionalidad se programa en lenguaje Java en otro lugar. Esto permite que al iniciar Eclipse no haya que cargar todos los plugins que le fueron instalados sino que tan solo lee estas listas logrando así arrancar muy rápidamente. Es recién cuando el usuario invoca alguno de estos plugins que se procede a cargarlo en memoria. La figura 2.7 muestra una imagen básica de una ventana de Eclipse para presentar sus elementos: Figura 2.7: Detalle de los componentes de la interfaz de usuario de Eclipse Como regla general, a todos los elementos destacados en la imagen se puede agregar funcionalidad. Pongamos algunos ejemplos: A la barra de menúes se pueden agregar nuevos menúes, y a estos nuevas opciones. A la barra de herramientas se pueden agregar nuevos botones. Se pueden agregar editores especializados en algún lenguaje en particular. Lo más importante (y lo que se ha hecho en el presente trabajo) es agregar nuevas ventanas (llamadas “Views” en la jerga). Estas pueden contener cualquier información de interés y se pueden hacer varias operaciones sobre ellas. En la sección 3.2 se expone en detalle la funcionalidad que se implementó. 15 Capítulo 3 Diseño e Implementación 3.1. Análisis de la tabla de símbolos Para extraer la información de la tabla de símbolos se eligió finalmente la herramienta “nm”. Su formato tabular hace que sea muy sencillo procesar la información y capturarla en el plugin. Para invocar a nm se usa la clase Shell (presentada en la figura 3.1), que tiene un único método estático “command”. El mismo genera un nuevo proceso para el sistema operativo que invoca al proceso recibido como parámetro. En este caso la llamada es “nm –print-size –size-sort –reverse-sort –radix=dec nombreArchivo.elf”. 1 3 public c l a s s Shell { s t a t i c A r r a y L i s t < S t r i n g > output ; p u b l i c s t a t i c A r r a y L i s t < S t r i n g > command ( f i n a l S t r i n g cmdline , final String directory ) throws IOException , I n t e r r u p t e d E x c e p t i o n { Process process = new P r o c e s s B u i l d e r ( new S t r i n g [ ] { " bash " , "−c " , cmdline }) . redirectErrorStream ( true ) . d i r e c t o r y ( new F i l e ( d i r e c t o r y ) ) . start () ; 5 7 9 11 13 output = new A r r a y L i s t < S t r i n g > ( ) ; BufferedReader br = new BufferedReader ( new InputStreamReader ( p r o c e s s . getInputStream ( ) ) ) ; 15 17 String line = null ; while ( ( l i n e = br . readLine ( ) ) ! = n u l l ) { output . add ( l i n e ) ; } 19 21 23 i f ( 0 ! = p r o c e s s . waitFor ( ) ) { return null ; } 25 27 r e t u r n output ; } 29 } Figura 3.1: Código de la clase “Shell”. 16 Capítulo 3. Diseño e Implementación Esta llamada devuelve una lista donde cada elemento es una línea de texto con una columna para cada dato que contiene, por ejemplo: 00032192 00000142 t VerificaFuentesGases Las mismas se interpretan de la siguiente manera: 1. Dirección de memoria del símbolo. En este plugin no se hace uso este dato ya que no es un requerimiento del mismo hacer un mapa topográfico de la memoria. En una segunda mirada, los microcontroladores organizan las secciones de código y datos en ciertos rangos de direcciones, con lo cual con de la dirección de memoria de un símbolo se puede inferir la sección en la que está ubicado. Esta posibilidad se descartó en este trabajo dado que impondría al usuario la necesidad de aprender el mapa de memoria del microcontrolador en uso y como se verá en breve la tercera columna da esta información. 2. Tamaño en memoria del símbolo. 3. Una letra que indica la sección de memoria donde el símbolo fue ubicado. 4. Nombre del símbolo. La figura 3.2 muestra como identifica nm las secciones [9]: "B" "b" The symbol is in the uninitialized data section (known as BSS). "D" "d" The symbol is in the initialized data section. "T" "t" The symbol is in the text (code) section. "?" The symbol type is unknown, or object file format specific. Figura 3.2: Códigos con los que el comando nm identifica cada sección de memoria. Para alojar estos datos se diseñó la clase Symbol, presentada en la figura 3.3: Se puede ver en el método “Symbol” el proceso de análisis, donde según el valor del carácter en la columna 3 (en el código se ve 2 porque cuenta desde 0) se asigna el nombre por el que los programadores conocen a las secciones. 3.2. Desarrollo del plugin A la hora de presentar la información al usuario, fue necesario decidir la mejor manera de hacerlo. Se optó por un esquema con 3 views: 1. Lista completa de símbolos 3.2. Desarrollo del plugin 2 4 17 p u b l i c c l a s s Symbol { p r i v a t e S t r i n g name ; private Integer size ; private String section ; p r i v a t e boolean enabled ; 6 p u b l i c Symbol ( S t r i n g name , I n t e g e r s i z e , S t r i n g s e c t i o n ) { t h i s . name = name ; this . size = size ; this . section = section ; t h i s . enabled = t r u e ; } 8 10 12 p u b l i c Symbol ( S t r i n g nmOutput ) { t h i s . enabled = t r u e ; 14 16 S t r i n g [ ] tokens = nmOutput . s p l i t ( " " ) ; t h i s . s i z e = I n t e g e r . p a r s e I n t ( tokens [ 1 ] ) ; t h i s . name = tokens [ 3 ] ; 18 20 i f ( tokens [ 2 ] . e q u a l s I g n o r e C a s e ( " b " ) ) t h i s . s e c t i o n = " BSS " ; e l s e i f ( tokens [ 2 ] . e q u a l s I g n o r e C a s e ( " d " ) ) t h i s . s e c t i o n = " Data " ; e l s e i f ( tokens [ 2 ] . e q u a l s I g n o r e C a s e ( " t " ) ) t h i s . s e c t i o n = " Text " ; e l s e i f ( tokens [ 2 ] . e q u a l s I g n o r e C a s e ( "w" ) ) t h i s . s e c t i o n = " Text−Weak" ; else t h i s . s e c t i o n = tokens [ 2 ] ; 22 24 26 28 30 } 32 @Override public String toString ( ) { r e t u r n name + " − " + s i z e ; } 34 36 p u b l i c boolean isInROM ( ) { r e t u r n s e c t i o n . e q u a l s ( " Text " ) || s e c t i o n . e q u a l s ( " Text−Weak "); } 38 40 p u b l i c boolean isInRAM ( ) { r e t u r n ! isInROM ( ) ; } 42 44 } Figura 3.3: Código de la clase “Symbol”. 2. Lista filtrada de símbolos 3. Treemap. Primero se introducirá cada una por separado con sus capacidades y luego se analizará la interacción entre ellas que es lo que genera el valor del plugin. 18 3.2.1. Capítulo 3. Diseño e Implementación Lista completa de símbolos Este es el punto de partida del desarrollo. Se optó por presentar el total de los símbolos identificados en el archivo binario en un formato de tabla con 3 columnas. En estas se presentan respectivamente el nombre del símbolo, su tamaño en memoria y la sección en la que fue ubicado por el compilador. Podría decirse que solo con esta lista se brinda una gran ayuda al programador. Como se ve en la figura 3.4, el objetivo principal de esta lista es presentar la visión total de los contenidos del archivo analizado. Figura 3.4: Lista completa de símbolos generada por el plugin Su principal característica es que cada símbolo tiene una casilla de verificación que indica si será graficado o no en el mapa y en la lista filtrada. Esto permite que el plugin presente información sobre la totalidad del archivo o solo sobre el subconjunto del mismo que sea de interés para el usuario. Además de mostrar los símbolos, se incluyeron dos ayudas para el usuario: un campo de búsqueda en el cual se puede escribir una parte del símbolo buscado y que la lista oculte los que no coinciden con él y un filtro que permite seleccionar todos los símbolos que correspondan a una sección en particular. De este modo es fácil ver el mapa de todos los símbolos, el mapa de la memoria de código, el mapa de la memoria de datos y una distinción entre dos secciones de la memoria de datos: las llamadas “bss” y “data” que representan los datos que no tienen un valor inicial y los que sí deben tenerlo respectivamente. 3.2.2. Lista filtrada de símbolos La figura 3.5 muestra la lista filtrada de símbolos. Esta lista es la que alimenta el treemap, todos los símbolos que esté mostrando son graficados. Si bien podría haberse mantenido oculta al usuario se decidió dejarla visible. 3.2. Desarrollo del plugin 19 El plugin puede funcionar con o sin ella habilitada. Incluye un totalizador que suma el tamaño de todos los símbolos mostrados. Figura 3.5: Lista filtrada de símbolos generada por el plugin 3.2.3. Treemap La vista treemap se puede apreciar en la figura 3.6. Muestra el mapa de árbol de los símbolos contenidos en la lista filtrada. Lo hace ordenando los símbolos por tamaño descendente, ya que se encontró que este es el criterio de mayor utilidad según los requerimientos del proyecto. En la imagen se pueden apreciar tres cuestiones importantes: Un símbolo muy grande en color gris oscuro que representa el espacio libre en memoria (si bien esta imagen no lo muestra, se verá luego en la interacción con la lista que sí lo hace). De esto se puede inferir que este microntrolador tiene capacidad de sobra para seguir agregándole funcionalidad. Un símbolo en verde oscuro en la parte inferior izquierda que ocupa una parte significativa comparado con las demás. De ser necesario liberar algo de memoria en esta aplicación, este sería el primer lugar a revisar. Una región en la esquina inferior derecha donde hay un sinnúmero de variables de tamaño muy pequeño. Es razonable suponer que son variables de 1 byte que representan estados de algún proceso en particular o contadores de 1 byte, con el rango de 0 a 256. Se ve que para recuperar el tamaño equivalente a tan solo UN símbolo de los de la parte superior derecha habría que eliminar muchos. Este es el punto mencionado previamente de los rendimientos decrecientes, donde no es provechoso aplicar un proceso de optimización. Si esta región fuese muy grande solo se puede concluir que el programador responsable de esta aplicación tiene algunos vicios que debe corregir. El mapa se dibuja con una paleta de 5 colores generada en la web [14]. Para que los rectángulos adyacentes se puedan diferenciar fácilmente entre 20 Capítulo 3. Diseño e Implementación Figura 3.6: Treemap generado por el plugin sí se tomaron dos medidas: Por un lado, cada símbolo se dibuja con un degradé entre su color base y uno que es un 20 % más oscuro, y por el otro se los dibuja con un recuadro negro fino. Para destacar el rectángulo que el usuario haya seleccionado, al mismo se le dibuja un reborde blanco grueso alrededor. 3.2.4. Interacciones entre las views El treemap es el objetivo principal del desarrollo, pero para que tenga una utilidad práctica es necesario tener control sobre lo que se está graficando. Para esto se decidió conectarlas de la siguiente manera: Se puede trabajar con solo un subconjunto de lo símbolos del archivo: Esto se logra destildando todos los símbolos de la lista completa y luego tildando los pocos que sean de interés. Esto último se ve ayudado por el campo de búsqueda de la lista completa. Cuando se quiere excluir un símbolo particular del análisis, se puede destildar únicamente ese y el mapa se redibujará sin él. Se puede concentrar el análisis en una sola sección, lo que se logra eligiéndola en el filtro de secciones de la lista completa. 3.2. Desarrollo del plugin 21 Se pueden ordenar ambas listas por nombre, por tamaño y por sección, lo que permite ver más fácil los símbolos de interés. Esto no modifica el treemap, que siempre se grafica ordenado por tamaño. Se puede seleccionar un rectángulo en particular en el treemap para ver a qué símbolo corresponde. Al hacer esto las listas lo muestran seleccionado y resaltado para que sea más fácil encontrarlo. 3.2.5. Obtención de la tabla de símbolos a graficar Para adquirir la tabla de símbolos se hace desde el plugin una llamada al comando “nm” usando la clase de Java “ProcessBuilder”. Esta se encarga de generar un nuevo proceso del sistema operativo que se encarga de ejecutar el comando “nm” con las opciones discutidas previamente y recoger la salida del mismo. El motivo de hacer esto fue mantener toda la funcionalidad en lenguaje Java y dentro del mismo plugin, para no imponer al usuario final la instalación de herramientas adicionales. 3.2.6. Modelo de los datos en memoria Al programar en un lenguaje orientado a objetos como es Java se simplifica mucho la organización de los datos en la memoria. Se empezó por definir una clase “Símbolo” que representa la información que se puede extraer del análisis de los datos: nombre, tamaño y sección. Luego se arma una lista con estos Símbolos, a la que se llama Modelo. 3.2.7. Presentación de los datos al usuario Las buenas prácticas de programación mandan que hay que separar el contenido de la presentación del mismo, de modo que se puedan alterar ambas por separado. En este caso, Eclipse hace uso copioso de los llamados “JFace viewers” [17]. Son numerosos visualizadores de datos a los que tan solo hay que darles una semblanza de cómo los mismos están organizados. La figura 3.7 se muestra el código (algo recortado) que genera la lista completa de símbolos para demostrar la sencillez que se puede trabajar una vez que se supera la curva de aprendizaje de estas herramientas: En la línea viewer.setContentProvider(new ArrayContentProvider()); se le dice a la tabla que los datos tienen forma de “Array” o vector, es decir que están todos uno a continuación del otro y con el mismo formato. Luego en viewer.setInput(Activator.getModel().getSymbols()); se le dice que sus datos de entrada provienen de la lista de símbolos contenida en el modelo. De la misma manera sencilla se ve como se programa el criterio para ordenar las listas por cualquiera de las tres columnas. 3.2.8. Opciones de configuración del plugin Se implementaron 4 opciones que permiten personalizar el funcionamiento del plugin: 1. Indicar la ruta hacia el comando “nm”. Esto es fundamental para que el plugin pueda funcionar en cualquier instalación que el usuario haya podido hacer de las herramientas de compilación. 22 1 3 Capítulo 3. Diseño e Implementación p r i v a t e void c r e a t e V i e w e r ( Composite p a r e n t ) { viewer = new CheckboxTableViewer ( new Table ( parent , createColumns ( parent , viewer ) ; ... ) ); viewer . s e t C o n t e n t P r o v i d e r ( new ArrayContentProvider ( ) ) ; viewer . s e t I n p u t ( A c t i v a t o r . getModel ( ) . getSymbols ( ) ) ; g e t S i t e ( ) . s e t S e l e c t i o n P r o v i d e r ( viewer ) ; 5 7 viewer . a d d C h e c k S t a t e L i s t e n e r ( new I C h e c k S t a t e L i s t e n e r ( ) { @Override p u b l i c void checkStateChanged ( CheckStateChangedEvent event ) { O b j e c t [ ] ce = viewer . getCheckedElements ( ) ; A c t i v a t o r . getModel ( ) . s e t E n a b l e d L i s t ( ce ) ; } }) ; 9 11 13 15 } 17 19 p r i v a t e void createColumns ( f i n a l Composite parent , f i n a l TableViewer viewer ) { S t r i n g [ ] t i t l e s = { "Name" , " S i z e " , " S e c t i o n " } ; i n t [ ] bounds = { 2 0 0 , 1 0 0 , 100 } ; 21 // name column TableViewerColumn c o l = createTableViewerColumn ( t i t l e s [ 0 ] , bounds [ 0 ] , 0 ) ; c o l . s e t L a b e l P r o v i d e r ( new ColumnLabelProvider ( ) { @Override p u b l i c S t r i n g g e t T e x t ( O b j e c t element ) { Symbol p = ( Symbol ) element ; r e t u r n p . getName ( ) ; } }) ; 23 25 27 29 31 // s i z e column // s e c t i o n column 33 } 35 37 39 41 43 45 47 p r i v a t e S e l e c t i o n A d a p t e r g e t S e l e c t i o n A d a p t e r ( f i n a l TableColumn column , f i n a l i n t index ) { S e l e c t i o n A d a p t e r s e l e c t i o n A d a p t e r = new S e l e c t i o n A d a p t e r ( ) { @Override p u b l i c void w i d g e t S e l e c t e d ( S e l e c t i o n E v e n t e ) { comparator . setColumn ( index ) ; i n t d i r = comparator . g e t D i r e c t i o n ( ) ; viewer . g e t T a b l e ( ) . s e t S o r t D i r e c t i o n ( d i r ) ; viewer . g e t T a b l e ( ) . setSortColumn ( column ) ; viewer . r e f r e s h ( ) ; } }; return selectionAdapter ; } Figura 3.7: Fragmentos del código que genera la lista completa de símbolos del plugin. 2. Indicar la ruta hacia el archivo binario que se analizará. Si bien las prácticas habituales en el desarrollo de plugins dictan que el mismo 3.2. Desarrollo del plugin 23 reconozca automáticamente en qué proyecto está trabajando el usuario y que de este mismo se pueda inferir cuál es el archivo a analizar, las limitaciones de tiempo no permitieron lograr esta funcionalidad y por esta razón debe indicarse al plugin cuál archivo se usará. Esta mejora quedó para un próximo avance del trabajo como se puede ver en la sección 5.2. 3. Tamaño de la memoria de código y datos del microcontrolador en uso. Estos dos valores son para completar la presentación de la información, permiten calcular el espacio libre en cada una. Para acomodar estas opciones fue necesario agregar un diálogo o pantalla de configuración del plugin. El mismo se muestra a continuación en la figura 3.8. Figura 3.8: Diálogo de opciones de configuración 3.2.9. Depuración del plugin El proceso de depuración es realmente sencillo, teniendo presente que se está generando un agregado a la propia plataforma de desarrollo y que la misma es sobresaliente a la hora de trabajar en el lenguaje Java. Para probar el plugin no se puede hacer otra cosa que iniciar una nueva ventana instancia de Eclipse donde el plugin está incorporado. Esto puede ser algo lento si el computador en el que se hace el desarrollo no tiene un hardware moderno. Al momento de escribir este trabajo el autor acababa de adquirir un disco rígido de estado sólido y se pudo apreciar la ventaja de los tiempos reducidos de carga que los mismos ofrecen. El otro aspecto importante es que debido a las características técnicas de Java, es posible reemplazar un pedazo del código en ejecución “en caliente”, es decir sin tener que detener el programa e iniciarlo de nuevo. Se puede concluir esta sección mencionando que la parte de manipulación de datos no tiene ninguna novedad para el progamador versado en programación orientada a objetos. La dificultad radica en aprender a usar correctamente las herramientas que ofrece la plataforma Eclipse, pero una 24 Capítulo 3. Diseño e Implementación vez logrado esto se ve que son lo bastante completas y poderosas como para que la programación se vea reducida a descubrir como indicar claramente a la plataforma la funcionalidad deseada. 3.3. Adopción de un algoritmo de treemapping La pieza final de la solución es finalmente el algoritmo de treemapping a usar. 3.3.1. Resolución gráfica El primer intento se hizo con una implementación propia del más elemental, “SliceAndDice”. El mismo funciona particionando el área gráfica en rectángulos en una sola dirección. Se asumía que el área donde se dibujaría sería más ancha que alta y por tanto cada fracción del tamaño total a representar se dibujaba mediante un rectángulo con orientación vertical donde su ancho representaba el alto del símbolo. Esto expuso inmediatamente la problemática de la representación gráfica: Supóngase que se está graficando en un rectángulo de 800x600 píxels un mapa de memoria de un microcontrolador de 512 Kbytes de memoria de código (una situación cotidiana, esa capacidad la tiene el LPC1769 que es el microcontrolador usado en el kit LPCXpresso usado ampliamente en numerosas universidades en los últimos años). Un sencillo cálculo arroja que hay que representar 524288 bytes en un rango de 800 píxels. Esto arroja unos 655 bytes por píxel, de lo que se puede inferir que una cantidad de memoria de menos de 655 bytes no amerita un píxel en la pantalla. Ese tamaño de memoria genera una muy mala resolución gráfica, en la práctica cotidiana lo suelen ocupar solamente los buffers de comunicación y algunas estructuras de control de ciertos protocolos con muchos campos de texto. Lo mismo ocurre en la sección de código, quedarían ocultas en el gráfico numerosísimas funciones y resultaría un análisis muy pobre de la ocupación de memoria. La situación cambia al partir el espacio como sugiere Schneiderman, alternando las dimensiones horizontal y vertical. En este caso un área de 800x600 píxels representa permite representar los 524288 bytes mediante 480000, quedando casi un pixel por byte. Es en este punto que se comenzó a buscar la adaptación de uno de los algoritmos implementados por la Universidad de Maryland en lugar de seguir con el propio. Estos algoritmos se pueden descargar libremente del Human-Computer Interaction Laboratory (HCIL) [5]. Al estar desarrollados en Java es sencillo integrarlos al plugin desarrollado. En este paquete de software se incluye una aplicación de demostración que genera una lista de 20 rectángulos con tamaños aleatorios distribuidos en forma Gaussiana y los dibuja en una ventana. Esto permite experimentar con los distintos algoritmos y elegir el más conveniente. Se recordará que Shneiderman [4] los clasifica según tres atributos que se analizan a continuación. 3.3. Adopción de un algoritmo de treemapping 3.3.2. 25 Relación de aspecto Shneiderman define la relación de aspecto de un rectángulo en su paper como “el mayor de ancho/alto y alto/ancho”, y luego dice “usando esta definición, cuanto menor sea la relación de aspecto de un rectángulo, más parecido a un cuadrado es. Un cuadrado tiene una relación de aspecto de 1, que es el mínimo valor posible.” Figura 3.9: Treemaps con una relación de aspecto promedio muy alta, producido por el algoritmo “Slice and dice”. A la izquierda aplicado a un solo nivel de datos. A la derecha, aplicado a datos organizados jerárquicamente. La relación de aspecto resulta la principal métrica de calidad de un treemap por dos motivos: Como se puede apreciar en la figura 3.9 resulta mucho más sencillo comparar tamaños relativos de cuadrados que de franjas. Los humanos tendemos a percibir la información visual buscando una suerte de “centro de masa” de la misma, o un punto central sobre el que se pueda sacar una conclusión rápida sin tener que explorar el detalle completo. Los cuadrados tienen esta propiedad, dado que se puede percibir rápidamente que la figura es cuadrada explorándola visualmente en cualquier dirección. El rectángulo sin embargo obliga a recorrer la dirección en la que es más largo hasta encontrar el borde. De acuerdo al análisis anterior sobre la resolución gráfica, los rectángulos con relación de aspecto muy alta obligan a dibujar franjas que corren el riesgo de caer por debajo de la resolución mínima y por ende desperdiciar pixels. 3.3.3. Preservación del orden de los datos La propiedad de ordenamiento de un treemap es su capacidad de preservar en la representación gráfica el orden que pudiera existir en los datos a mostrar. Al analizar este punto debe tenerse presente el uso que se da a los treemaps como herramienta de visualización. Schneiderman dice en su paper: 26 Capítulo 3. Diseño e Implementación “muchos conjuntos de datos contienen información de ordenación que es útil para ver patrones o para localizar objetos particulares en el mapa. Por ejemplo, los datos de bonos descritos en Johnson[1994] están ordenados naturalmente por fecha de madurez y tasa de interés. En muchos otros casos, el orden de los datos es alfabético”. Haciendo foco en los datos que se buscaron representar en este trabajo, se puede ver que en el mapa la información de nombre del símbolo no genera una ordenación que se deba preservar, y lo mismo pasa con la sección de memoria a la que pertenece cada uno ya que es muy sencillo dibujar un mapa para cada una haciendo uso del campo de filtrado. En una futura versión del plugin desarrollado sería util poder mostrar el agrupamiento de los símbolos según el archivo en el que fueron declarados y estos según la biblioteca a la que pertenecen. En ese caso sí sería de interés elegir un algoritmo de treemapping que preservara este orden, pero esta posibilidad queda por fuera del presente trabajo. 3.3.4. Estabilidad de los datos La estabilidad de los datos se refiere a cuánto cambian de posición los rectángulos en un mapa al cambiar sus datos de entrada. Esto debe pensarse para el caso de visualizar datos dinámicos como por ejemplo cotizaciones de acciones de la bolsa de valores. Una buena estabilidad hace que al mirar y asimilar un mapa, si sus datos cambian de alguna manera un símbolo representando un valor en particular no se mueva drásticamente en el mapa. Se dice que un algoritmo es inestable cuando un cambio en el tamaño de un rectángulo hace que el mismo deba dibujarse en un lugar distinto (imagínese la progresión desde la esquina inferior derecha a la superior izquierda de un rectángulo a medida que crece en tamaño). Si bien es una propiedad muy importante, en el trabajo presente los datos a representar no tienden a cambiar bruscamente salvo cuando se está optimizando la asignación de la memoria. 3.3.5. Algoritmo elegido Habiendo ponderado las tres propiedades de los cinco algoritmos disponibles, se eligió “Squarified” como el más apropiado por ser el que da mejor relación de aspecto. Esta característica se juzgó de mayor importancia que las otras dos. En la figura 3.10 se muestra el proceso mediante el cual el algoritmo particiona el espacio disponible en subrectángulos, buscando la mejor relación de aspecto y perdiendo cualquier otro orden que los datos pudieran tener. 3.3. Adopción de un algoritmo de treemapping Figura 3.10: Particionado del espacio disponible en rectángulos, según el algoritmo “Squarify”. Figura tomada de [6] 27 29 Capítulo 4 Ensayos y Resultados 4.1. Precisión del análisis de la tabla de símbolos El presente trabajo se basó en la siguiente hipótesis: La suma de la totalidad de los tamaños en memoria reportados por nm para los símbolos de cada sección tiene como resultado el tamaño total ocupado en la memoria del microcontrolador. Para esta hipótesis se realizó el siguiente ensayo. 4.1.1. Precisión de la identificación de secciones de la herramienta nm Se ha dicho en la subsección 2.2.2 que la herramienta nm asigna una letra (’t’, ’b’ o ’d’) a cada símbolo que encuentra en su análisis de la tabla de símbolos y que cada una corresponde a una sección. En el apéndice A se presenta un programa de prueba con símbolos de distintos tamaños ubicados en las distintas secciones, usado como patrón de referencia para verificar que el reporte es correcto. El lector puede buscar la definición de los segmentos .text, .bss y .data en [2] y [3]. De todos modos se presenta a continuación una breve reseña: .text Sección de memoria donde se ubican las instrucciones de máquina a ejecutar por el microprocesador. Esta sección de memoria no se modifica durante la ejecución del programa (existen excepciones a esta regla pero su discusión no es relevante a este ensayo). .bss Sección de memoria reservada a variables que no tienen un valor inicial dado. .data Sección de memoria reservada a variables que sí tienen un valor inicial dado por el programador. Este valor inicial se almacena en la memoria de programa (sección .text) y tiene un impacto en la misma. Los valores reportados por el plugin desarrollado se contrastaron contra la herramienta “size” [12], cuyo propósito es listar “los tamaños de las secciones -y el tamaño total- de cada objeto (. . . ) en su lista de argumentos. . . )”. La salida del mismo se muestra en la figura 4.1 como ejemplo, con la salvedad de que los valores reportados son arbitrarios y no deben tomarse como parte del ensayo. El programa de prueba se generó partiendo de un asistente (wizard) para un nuevo proyecto utilizando las librerías LPCOpen en el entorno LPCXpresso 8.1.4, e incluye las librerías estáticas “lpc_chip_ 175x_6x” y “lpc_board_nxp _lpcxpresso_1769”. Luego se declararon las variables necesarias para el ensayo. De acuerdo a Erich Styger [16], el resultado esperado es el siguiente: 30 Capítulo 4. Ensayos y Resultados arm-none-eabi-size "TestProject1.axf" text data bss dec hex filename 4004 200 8 4212 1074 TestProject1.axf Figura 4.1: Imagen de la salida del size al analizar el programa de prueba. En la sección .bss se espera que la suma de los tamaños reportados por nm ocupe exactamente el tamaño reportado por el comando size. En la sección .data se espera lo mismo. En la sección .text hay que ponderar otras cuestiones que ponderar, ya que se sabe que a veces los compiladores organizan las funciones en direcciones de memoria múltiplo de 2 o 4 para optimizar el acceso a las funciones y sobre esto no se encontró mención en la documentación del comando “size”. También hay una situación con las declaraciones “Weak”, que son funciones definidas con cierto nombre pero que luego el programador puede sobreescribir (es decir, agregar una segunda definición de esa función) y no está claro cómo se contabiliza esto tampoco. En este caso, el ensayo consiste en inspeccionar los totales reportados por el plugin y compararlos contra la salida del comando size. 4.1.2. Resultados del ensayo La suma de los tamaños de las variables ubicadas en la sección .bss tuvo un error de 3 bytes. Como se puede ver en la figura 4.2, size reportó 172 bytes y el plugin identificó lo esperado por el programador que fueron 165. 4 de esos 172 se encuentran en la variable SystemCoreClock, que no la declara el programador sino una de las librerías que incluye el programa de ejemplo. La figura 4.3 muestra que en la sección .data se encontró la misma discrepancia de 3 bytes. En ambos casos (.bss y .data ) se considera despreciable el error de 3 bytes por la siguiente razón: Al trabajar con procesadores con una arquitectura de 32 bits, una declaración de variable de tipo int (el valor genérico de un entero sin aplicarle ningún calificador ni modificador) ocupa 4 bytes, por esta razón podría pensarse que 4 bytes es a efectos prácticos la resolución de la medición o la mínima variación del dígito menos significativo. Por lo tanto una discrepancia inferior a 4 bytes no afecta la medición de ninguna manera. La figura 4.4 deja ver que en la sección .text sí se registraron algunas discrepancias, a saber: 1. El comando size reportó 4204 bytes. 2. La sumatoria de los tamaños reportados en la sección .text fue de 3846 bytes. 4.1. Precisión del análisis de la tabla de símbolos 31 Figura 4.2: Vista de la sumatoria del tamaño de los símbolos en la sección .bss Figura 4.3: Vista de la sumatoria del tamaño de los símbolos en la sección .data 3. El plugin por ahora no identifica símbolos con el atributo Weak, pero una exploración manual del archivo binario con el comando nm mostró 274 bytes en símbolos con ese atributo. Obviando las declaraciones “weak”, la diferencia es de 358 bytes o del 8,5 %. Si en cambio se suman esos 274 bytes la discrepancia baja a 84 bytes o un 2 %. Debe notarse que la suma reportada por el plugin es en ambos casos menor a lo reportado por el comando size, con lo cual se puede inferir que el mismo contabiliza otras cosas además de la sumatoria de los símbolos ubicados en dicha sección. No se encontró documentación acerca de cómo exactamente contabiliza el tamaño de la sección .text el comando size. Esto impidió incrementar la precisión del análisis más allá del 8,5 % reportado en este caso. En una futura revisión de este trabajo se puede encontrar esta información haciendo 32 Capítulo 4. Ensayos y Resultados Figura 4.4: Vista de la sumatoria del tamaño de los símbolos en la sección .text un análisis del código fuente del mismo, disponible en [8]. Esta posibilidad quedó fuera del presente trabajo debido a limitaciones de tiempo. Finalmente, se concluye que si bien un 8,5 % de diferencia no es lo bastante bueno como para dimensionar un microcontrolador para una aplicación, no se afecta el cumplimiento de ninguno de los requerimientos del proyecto. 4.2. Consumo de recursos de hardware causado por el plugin Otro ensayo que se consideró relevante realizar es evaluar el impacto que el plugin causa en los recursos del computador en uso. Se midió el tiempo de procesamiento que lleva dibujar el treemap y se descartó la posibilidad de medir la memoria solicitada por el plugin. Las aplicaciones en Java se ejecutan en una instancia de la JVM (Máquina Virtual Java) con una cantidad de memoria disponible predefinida, y durante el desarrollo del plugin la misma nunca se agotó. Al no haber sido necesario agrandar la cantidad de memoria disponible para la JVM en cuestión se concluyó que el consumo de memoria no resulta problemático. 4.2.1. Medición del tiempo de procesamiento Para esta medición se usa el método “nanoTime” incluído en el lenguaje Java [7]. El mismo “Devuelve el valor actual del temporizador más preciso del sistema, en nanosegundos”. Este ensayo no buscó una medición exacta sino conocer el orden de magnitud del tiempo invertido para determinar si el uso del plugin entorpece la labor cotidiana del programador. El ensayo se realizó en las siguiemtes condiciones: 4.3. Validación con usuarios finales 33 Hardware: Computador portátil Asus K455L [15], al que se agregaron 4GB de memoria RAM adicionales para un total de 12Gb y se le reemplazó el disco rígido mecánico por uno de estado sólido Samsung EVO 850 de 250Gb. Sistema operativo: Ubuntu Mate 16.04, 64 bits, con el kernel 4.6.0040600rc6-generic. Versión de Java: openjdk version “1.8.0_91”. Versión de Eclipse: Mars 4.5.2. Se compiló el programa de prueba como figura en el apéndice A. El procedimiento de ensayo fue el siguiente: 1. Se tomó el tiempo actual del sistema con la funcion nanoTime. 2. Se provocó el redibujo del mapa dentro de un lazo cien veces. 3. Se tomó el tiempo final del ensayo. 4. Se lo dividió por cien. 5. Se repitieron los puntos anteriores 30 veces y se anotaron los resultados. 6. Se buscaron los valores mínimo y máximo. 7. Se sacó el promedio y el desvío estándar. 4.2.2. Resultados del ensayo Se observaron los siguientes valores (en microsegundos): 453, 612, 436, 819, 463, 729, 752, 429, 499, 772, 396, 504, 534, 761, 406, 400 393, 792, 403, 392, 409, 408, 395, 393, 389, 395, 436, 476, 392, 390. Las mediciones presentan un promedio de 501 uS, y un desvío estándar de 147 uS. El valor mínimo es de 389 uS y el máximo es de 819 uS. El promedio se ponderó contra el tiempo que conlleva el proceso de compilación (en la corrida de prueba fue reportado en 324ms) y resultó despreciable. En segunda instancia se aplicó un criterio más estricto que fue el de redimensionar la ventana que contiene el mapa (esto implica un redibujo de la misma así sea que el tamaño cambia en un solo píxel) y no se observó ningún enlentecimiento de la interfaz. Si bien esta apreciación es puramente subjetiva se la puede tomar como un criterio de máxima y el resultado es satisfactorio. 4.3. Validación con usuarios finales El paso final para la validación de un software nuevo es la llamada “Beta-test”: una prueba previa a su distribución realizada por parte de una muestra de los usuarios finales. A los usuarios que participan de esta prueba se los llama “Beta-testers”. 34 Capítulo 4. Ensayos y Resultados Se dice que el software está en estado “Beta” cuando no ha sido utilizado aún por su público objetivo pero que ya está lo bastante maduro como para un primer contacto con el mismo. Es fundamental hacer estas pruebas porque los usuarios no tienen el conocimiento interno del software que tienen los desarrolladores del mismo, y por lo tanto tienen una perspectiva imparcial sobre el uso y capacidades del mismo. De estas pruebas se espera que los testers descubran defectos y/o aporten nuevas perspectivas que hayan escapado a la óptica del programador. Cuando el software no ha llegado aún a este punto se lo llama “Alfa”. Un software en este estado funciona bien en la medida que lo use una persona que conoce los defectos y limitaciones del mismo y lo use dentro de estas. Tanto en la industria del software como en la comunidad del software libre, se suele buscar que las “Beta-tests” lleguen a la mayor cantidad de usuarios posibles y en algunos casos se pide a los “Beta-testers” que previo a su participación informen algunos datos personales. Esto se hace para tener un volumen significativo de realimentación acerca del uso del software y poder ponderarlo de acuerdo al perfil de los testers. Si bien este es el procedimiento que aporta mayor valor estadístico, requiere de un equipo de personas que procese los reportes recibidos y que pueda actuar en consecuencia. Al ser este un desarrollo académico hecho por un solo programador se optó en cambio por armar un “focus-group”, o una pequeña muestra formada por un grupo reducido de usuarios con perfiles bien diferenciados que represente a un grupo genérico de usuarios. A quienes se les envió el software para evaluar también se les sugirió el siguiente cuestionario con impresiones que se buscaban recabar acerca del uso del software: 1. La instalación es confusa o se parece a cualquier otro plugin? 2. En qué variante y versión de Eclipse lo instalaron? 3. Las instrucciones le sirvieron para instalar y usar el software? 4. El uso del software resulta intuitivo? 5. El software le resultó útil? 6. El uso del software hizo que se ponga lento el entorno donde fue instalado? 7. Alguna otra impresión que le haya causado? Los siguientes son los reportes que se alcanzaron a recibir a la fecha de cierre de este trabajo, sin ningún orden particular. 4.3.1. Esp. Ing. Pablo Ridolfi Pablo Ridolfi es Ingeniero en electrónica, graduado de la UTN-FRH. También tiene el título de Especialista en Sistemas Embebidos. Actualmente es coordinador general del proyecto CIAA. 4.3. Validación con usuarios finales 35 1. Tuve que deshabilitar la opción de mostrar por categorías, sino no aparece nada. Salvo eso (fijate si lo podés corregir y te vas a ahorrar un montón de comentarios tipo "no me aparece nada") instaló perfecto, avisando que no está firmado digitalmente. 2. LPCXpresso v8.1.4 [Build 606] [2016-03-14] 3. Sí, sirvieron, al menos yo lo entendí, pero es obvio que no somos novatos. Deberías mandarle esto a un alumno que recién está empezando para ver si lo puede hacer. 4. Sí, pero idem anterior. 5. Creo que si estuviera justo de recursos y necesito hacer un análisis para priorizarlos me resultaría útil. 6. No. 7. Estaría bueno poder hacer zoom en el treemap. Cuando hay mucha RAM/Flash libre lo demás te queda muy chiquito, aunque se pueden deshabilitar las áreas “Free ROM” y “Free RAM”. Al menos agregaría una opción tipo toolbar sobre el treemap para deshabilitarlas rápidamente y ver sólo lo que está ocupando espacio. Por lo demás me parece muy copado lo que hiciste, felicitaciones! 4.3.2. Ing. Mirko Serra Mirko Serra es Ingeniero en electrónica, graduado de la UTN-FRBA. Se dedica al desarrollo de sistemas embebidos en forma independiente. 1. Hay que asegurarse de que no esté tildado Group items by category para que lo muestre cuando se instala localmente. En el caso del servidor lo muestra dentro de la categoría ESMP. 2. LPCXpresso v8.1.4 [Build 606] [2016-03-14]. 3. Lo quise instalar en el Eclipse que tenía funcionando y no andaba. Resulta que es una versión vieja. Me deja instalarlo pero no encontraba el Memory Profiler. 4. No encontré la forma fácil de visibilizar todo de una. Vine a la captura que me mandaste vos y ahí pude visibilizarlos poniendo en Quick Access una por una las vistas de Treemap Views, Symbol List, Filtered Symbol List. 5. Resulta MUY práctico el manejo general. Elijo Text y tengo una idea del uso de memoria (libre vs ocupada); hago click en el cuadrado grande, lo destildo y saqué la memoria libre de la ecuación y puedo ver el uso desde el programa. 6. No. 7. La pestaña Memory Profiler aparece vacía. Sería bueno que apareciera convalores por defecto. 37 Capítulo 5 Conclusiones 5.1. Grado de cumplimiento de los requerimientos del proyecto Para beneficio del lector se reproducen los 4 objetivos originales del proyecto, luego se analiza el grado de cumplimiento de cada uno en particular: 1. Representación visual del uso de la memoria interna del MCU. 2. Fácil identificación de los principales consumos de memoria. 3. Indicación de a qué zona de memoria fue asignada cada variable. 4. Presentación de estos datos dentro de una ventana de Eclipse. La figura 5.1 muestra el resultado final del plugin y sirve de sustento al análisis siguiente. Figura 5.1: Resultado final del desarrollo 5.1.1. Representación visual del uso de la memoria interna del MCU El primer objetivo se considera perfectamente logrado. Al incluirse un símbolo representando la memoria disponible (el recuadro oscuro de mayor tamaño), al usuario no le quedan dudas de que lo que está mirando 38 Capítulo 5. Conclusiones inicialmente es la memoria completa del microprocesador, y con esto en mente, se pueden hacer rápida y visualmente varios análisis: Determinar el grado de ocupación del CPU, algo sumamente útil en el desarrollo para saber si el microprocesador está bien dimensionado para el desarrollo o si será menester cambiarlo. Discernir si es provechoso optimizar el uso de memoria de la aplicación. Se sabe que todo proceso de optimización tiene un punto de rendimientos decrecientes, esto es un punto a partir del cuál el tiempo invertido en mejorarlo rinde menos que los beneficios obtenidos. En el caso de una aplicación con microprocesador, este es el punto en el que el gasto principal de memoria no está concentrado en unos pocas variables sino que hay numerosas variables que ocupan 4 o 2 bytes cuando podrían haber ocupado 2 o 1. Si resultara provechoso hacer un proceso de optimización, sería evidente el punto de partida del mismo. 5.1.2. Fácil identificación de los principales consumos de memoria El segundo objetivo también se considera logrado al 100 %. El autor considera que la representación visual mediante tamaños relativos es mucho más rápida de asimilar que extraer información numérica de una tabla, tenga esta el formato que tenga. Como ejemplo de esto se puede citar el diseño de los tableros de mandos de las aeronaves comerciales, en el que un piloto debe monitorear una cantidad apreciable de indicadores y por esta razón se prefieren indicadores del tipo de aguja en lugar de indicadores numéricos. Esto último permite hacer una rápida inspección visual y detectar rápidamente una aguja con una inclinación distinta a las demás. Adicionalmente, el proceso de obtener información numérica para procesarla conlleva un pequeño esfuerzo mental que si bien es despreciable en cada ocasión, a lo largo de un día completo de trabajo acumula un gasto que redunda en un cansancio adicional y una caída en la productividad del programador. 5.1.3. Indicación de a qué zona de memoria fue asignada cada variable El objetivo número 3 también está logrado. Esta información se ve no en los mapas gráficos sino en las listas de símbolos completa y filtrada, ambas tienen una columna llamada “Section” donde se expone el nombre de la sección en la que el compilador ubicó cada símbolo. Este objetivo no se ha logrado en la misma medida que los demás, ya que esta ubicación sí se debe obtener de una tabla con los perjuicios mencionados anteriormente. Se podría haber utilizado una segunda paleta de colores para diferenciar las distintas secciones en el mapa gráfico, pero se consideró que no reportaría un beneficio significativo. Esto sumado a la falta de tiempo hizo que esta funcionalidad quedara por fuera del proyecto. 5.2. Próximos pasos 5.1.4. 39 Presentación de estos datos dentro de una ventana de Eclipse El último objetivo se logró también por completo. La figura 5.1 habla por sí misma en este punto. 5.2. Próximos pasos Si bien el proyecto logró sus objetivos iniciales, durante el desarrollo se identificaron varias posibilidades de mejora que el tiempo no permitió abordar, pero que de todos modos se listan a continuación con la esperanza de que este trabajo pueda ser continuado por otra persona o por el mismo autor en el camino de una maestría. Mejorar la precisión de la medición de la sección .text Como se mencionó en la sección 4.1.1 la medición de la ocupación de la memoria de código tiene un error apreciable, pero hay fuentes de información que permiten seguir investigando este tema cuando el tiempo lo permita. Extensión al lenguaje C++ El mismo presenta algunos desafíos a la hora de identificar los nombres de los símbolos ya que el compilador los deforma en pos de soportar algunas características avanzadas del lenguaje. Esta dificultad se evitó en esta instancia del trabajo. Agrupación jerárquica de librerías y módulos El algoritmo actual dibuja los símbolos ordenados por tamaño, sin que se pueda apreciar en qué archivo fueron declarados. A veces en el desarrollo puede ser útil ver la jerarquía de qué símbolos provienen de qué archivos y a su vez qué archivos forman qué bibliotecas de código. Esto se obvió ya que no forma parte de los requerimientos del proyecto, pero resulta una buena adición en una próxima etapa. Distribución automatizada En la jerga se le llama a esto “deployment”. En este ámbito se refiere al proceso mediante el cual se construye y empaca el software para su distribución a los usuarios finales del mismo. Actualmente esto se hace con unos cuantos procedimientos manuales que resultan algo tediosos. Se experimentó someramente con programas para hacer la distribución automáticamente pero no se los pudo poner en práctica por falta de tiempo. Mejorar la integración del plugin con la interfaz de usuario de Eclipse El programador con experiencia en el uso de Eclipse como herramienta de programación seguramente notará que si bien el plugin desarrollado cumple con su función, resuelve algunas cuestiones de forma diferente a lo esperado. Esto se debe a la inexperiencia desarrollando plugins del autor de este trabajo (recuérdese que este es el primero que escribe), en un próximo avance esta situación debería remediarse. Representación gráfica de vectores y estructuras como mapas anidados Para beneficio del lector inexperto en software se recuerda que los vectores son una suerte de listas de elementos donde todos son del mismo tipo, y que las estructuras son agrupaciones de elementos que pueden 40 Capítulo 5. Conclusiones ser de distintos tipos. Lo que ambos tienen en común es que son conjuntos de varios elementos, y en su representación en el mapa podría hacerse esta distinción. Esta posibilidad se descartó porque no aporta a ninguno de los requerimientos iniciales del proyecto, servirían como un refinamiento gráfico. I18N I18N es una suerte de abreviatura de la palabra inglesa “internationalization” (se cuentan 18 letras entre la ’I’ y la ’N’) y representa el proceso de traducir fácilmente una aplicación a varios idiomas. En el plugin actual no se contempló esta posibilidad sino que se optó por usar el idioma inglés en la interfaz de usuario para llegar a la mayor cantidad de usuarios posibles. En un futuro desarrollo se debería poder traducir la interfaz de usuario a otros idiomas. 41 Apéndice A Programa usado para los ensayos de medición 42 2 4 // // // // Apéndice A. Programa usado para los ensayos de medición Name Author Version Description : : : : TestProject1 . c Ing . Alejandro Celery 1.0 Programa para ensayar mediciones de nm 6 8 # i n c l u d e " board . h " # i n c l u d e < c r _ s e c t i o n _ m a c r o s . h> # i n c l u d e < s t d i n t . h> 10 # d e f i n e N 10 12 14 16 18 20 int8_t int16_t int32_t int64_t int8_noInit ; int16_noInit ; int32_noInit ; int64_noInit ; int8_t int16_t int32_t int64_t i n t 8 _ n o I n i t _ v e c 1 0 [N] ; i n t 1 6 _ n o I n i t _ v e c 1 0 [N] ; i n t 3 2 _ n o I n i t _ v e c 1 0 [N] ; i n t 6 4 _ n o I n i t _ v e c 1 0 [N] ; int8_t int16_t int32_t int64_t int8_init int16_init int32_init int64_init int8_t int16_t int32_t int64_t i n t 8 _ i n i t _ v e c 1 0 [N] i n t 1 6 _ i n i t _ v e c 1 0 [N] i n t 3 2 _ i n i t _ v e c 1 0 [N] i n t 6 4 _ i n i t _ v e c 1 0 [N] 22 24 26 28 30 = = = = 0 x12 ; 0 x1234 ; 0 x12345678 ; 0 x1234567812345678 ; = = = = { { { { 0 x12 } ; 0 x1234 } ; 0 x12345678 } ; 0 x1234567812345678 } ; 32 i n t main ( void ) { 34 SystemCoreClockUpdate ( ) ; Board_Init ( ) ; Board_LED_Set ( 0 , t r u e ) ; 36 38 i n t i = 0 ; // V a r i a b l e l o c a l , no s a l e en e l r e p o r t e 40 while ( 1 ) for ( i if ( if ( if ( if ( 42 44 46 if if if if 48 50 ( ( ( ( { = 0 ; i < N; ++ i ) { int8_init > 0 ) int8_noInit = int16_init > 0 ) int16_noInit int32_init > 0 ) int32_noInit int64_init > 0 ) int64_noInit 1; = 1; = 1; = 1; int8_init_vec10 [ i ] > 0 ) int8_noInit_vec10 [ i ] = int16_init_vec10 [ i ] > 0 ) int16_noInit_vec10 [ i ] int32_init_vec10 [ i ] > 0 ) int32_noInit_vec10 [ i ] int64_init_vec10 [ i ] > 0 ) int64_noInit_vec10 [ i ] } } return 0 ; 52 54 } Figura A.1: Programa usado para contrastar los reportes del plugin contra el comando size. 1; = 1; = 1; = 1; 43 Bibliografía [1] Artículo de Wikipedia sobre Eclipse. [Online]. Disponible: https : / / es . wikipedia . org / wiki / Eclipse _ (software). Fecha de última actualización. 2016. [2] Artículo de Wikipedia sobre el segmento de código. [Online]. Disponible: https://en.wikipedia.org/wiki/Code_segment. Fecha de última actualización. 2016. [3] Artículo de Wikipedia sobre el segmento de datos. [Online]. Disponible: https://en.wikipedia.org/wiki/Data_segment. Fecha de última actualización. 2016. [4] B. B. Bederson, Ben Shneiderman y Martin Wattenberg. «Ordered and Quantum Treemaps: Making Effective Use of 2D Space to Display Hierarchies». En: ACM Transactions on Graphics 21.12 (oct. de 2002), págs. 833-854. URL: http://portal.acm.org/citation.cfm? doid=571647.571649. [5] Ben Bederson y Martin Wattenberg. Librería de algoritmos de treemapping. [Online]. Disponible: www . cs . umd . edu / hcil / treemap history/Treemaps-Java-Algorithms.zip. [6] Mark Bruls, Kees Huizing y Jarke van Wijk. «Squarified Treemaps». En: In Proceedings of the Joint Eurographics and IEEE TCVG Symposium on Visualization. Press, 1999, págs. 33-42. [7] Documentación de la clase System, método nanoTime. [Online]. Disponible: http://docs.oracle.com/javase/1.5.0/docs/api/ java/lang/System.html. Fecha de consulta. 2016. [8] Index of /gnu/binutils. [Online]. Disponible: https : / / ftp . gnu . org/gnu/binutils/. Fecha de consulta. 2016. [9] Manpage del comando nm. [Online]. Disponible: https://sourceware. org / binutils / docs / binutils / nm . html. Fecha de consulta. 2016. [10] Manpage del comando objdump. [Online]. Disponible: https://sourceware. org/binutils/docs/binutils/objdump.html. Fecha de consulta. 2016. [11] Manpage del comando pahole. [Online]. Disponible: http://linux. die.net/man/1/pahole. Fecha de consulta. 2016. [12] Manpage del comando size. [Online]. Disponible: https://sourceware. org/binutils/docs/binutils/size.html. Fecha de consulta. 2016. [13] Ben Shneiderman. Treemaps for space-constrained visualization of hierarchies. [Online]. Disponible: http : / / www . cs . umd . edu / hcil / treemap-history/. Fecha de primera publicación. 1998. 44 BIBLIOGRAFÍA [14] Sitio web coolors.co. [Online]. Disponible: https://coolors.co/ app. Fecha de consulta. 2016. [15] Sitio web de Asus, descripción del producto usado para el ensayo. [Online]. Disponible: https://www.asus.com/latin/Notebooks/ X455LJ/specifications/. Fecha de consulta. 2016. [16] text, data and bss: Code and Data Size Explained. [Online]. Disponible: https://mcuoneclipse.com/2013/04/14/text-data-andbss- code- and- data- size- explained. Fecha de publicación. 2013. [17] Lars Vogel y Simon Sholz. Eclipse JFace Overview - Tutorial. [Online]. Disponible: http://www.vogella.com/tutorials/EclipseJFace/ article.html. Fecha de publicación. 2014. [18] Windirstat. [Online]. Disponible: https://windirstat.info/.