pDI-Tools: Mecanismo de interposición dinámica de código | ||
---|---|---|
Anterior | Capítulo 6. Configuración de pDI-Tools | Siguiente |
pDI-Tools incorpora la posibilidad de ser configurado mediante ficheros de texto. Este método no sólo facilita la configuración, sino que es mucho más fácil de controlar y ofrece mucha más potencia que las variables de entorno.
Un fichero de configuración se estructura en secciones. La secciones son bloques dentro de un fichero de configuración. Estos bloques son indivisibles, y por lo tanto todas las secciones se encuentran al mismo nivel. Estas secciones permiten dividir el fichero de configuración en porciones que podemos activar, desactivar y reutilizar fácilmente.
En un fichero de configuración, dentro de las secciones, podemos encontrar los siguientes elementos:
Comentarios. Nos permiten documentar las decisiones de configuración o desactivar temporalmente determinadas opciones.
Asignaciones. Están limitadas a una sola línea en la que a un determinado parámetro le asignamos un valor. Los parámetros están predefinidos y es un error referirse a un parámetro que no existen. Los valores válidos dependen del tipo de parámetro.
Por regla general, ha de tenerse en cuenta que si se realiza más de
una vez la misma asignación, el valor asignado será el de la última
asignación procesada. Existen excepciones (como config
)
que pueden definirse varias veces, creándose una lista de valores.
Acciones.
Cuando se encuentra uno de estos elementos se ejecuta la acción
correspondiente. Las acciones nunca reciben parámetros. Por
ejemplo reset_config
destruye la lista construída hasta
el momento con el parámetro config
.
Comandos. Son palabras reservadas del lenguaje de ficheros de configuración. Permiten incluir ficheros de configuración dentro de un fichero de configuración, abortar la lectura o emitir mensajes.
Un comentario se reconoce porque el primer carácter de la línea que no sea un blanco es una almohadilla #.
Las secciones, tal como hemos explicado, dividen un fichero de configuración en diferentes bloques. Una sección comienza con el identificador de sección, encerrado entre corchetes, y termina con el comienzo de una nueva sección (con otro identificador de sección), o con el propio fin del fichero de configuración.
En todo fichero de configuración siempre existe la sección implícita global. Si no se indica, al comenzar un fichero, ningún nombre de sección, se asume que todo pertenece a esta sección. También es posible introducir contenidos en ella explicitamente abriendo una sección con su nombre.
Seguidamente mostramos un fichero de configuración de ejemplo, comentado, dividido en tres secciones: global (de forma implícita), sección_de_ejemplo y otra_sección:
# .. asignaciones y comandos de la sección 'global' .. [seccion_de_ejemplo] # .. asignaciones y comandos de la sección 'seccion_de_ejemplo' .. [otra_sección] # .. asignaciones y comandos de la sección 'otra_sección' ..
Un último detalle es que las secciones pueden estar partidas por otras secciones dentro de un mismo fichero, pero cuando se cargue el fichero se procesarán como un todo todos los trozos de la sección sin verse afectada por estas divisiones. Por ejemplo, supongamos que queremos recoger la configuración contenida en la sección AAA del siguiente fichero de configuración:
[AAA] # .. asignaciones y comandos de la sección 'AAA' .. [BBB] # .. asignaciones y comandos de la sección 'BBB' .. [AAA] # .. MAS asignaciones y comandos de la sección 'AAA' ..
El resultado final es que se procesará la primera división con el nombre AAA, se ignorará el contenido de la sección BBB, y cuando se vuelva a encontrar la sección AAA se seguirá procesando el fichero hasta el final.
Las asignaciones se componen de un parámetro y un valor separados por un signo de igualdad (=). Una asignación debe ocurrir en su totalidad dentro de una misma línea.
Ambos lados de una asignación pueden estar entre comillas dobles que delimiten sus campos. Los espacios a los lados del signo de igualdad se ignorarán y no formarán parte ni del parámetro ni del valor. Si se delimita el parámetro o el valor mediante comillas es posible incluir comillas escapándolas con una contrabarra (\). Ejemplo de asignaciones:
[asignaciones] debug = 1 be_path = "/home/gerardo/backends:/usr/local/pdi-tools/backends" "mi parametro" = "Toma como valor este \"string\" entre comillas"
Las siguientes herramientas de configuración de que disponemos son los comandos. Actualmente hay los siguientes:
Include {fichero [:sección] | :sección}
Se sustituye por el contenido de la sección global o
sección
del fichero
fichero
. Si no se especifica el fichero
se busca la sección en el fichero actual. Debe vigilarse con
esta opción porque actualmente no hay ningún control de
recursiones y ¡es posible entrar en bucles infinitos de
inclusiones!
Si en sección
se especifica el valor
%PLATFORM% se incluye el contenido de la sección que
tenga el nombre de la plataforma en uso.
Log {mensaje}
Imprime el mensaje mensaje
en el
"log" de ejecución de pDI-Tools.
Warning {mensaje}
Emite el mensaje de advertencia mensaje
en el "log" de ejecución de pDI-Tools.
Error {mensaje}
Imprime el mensaje de error mensaje
en
el "log" y termina la ejecución de pDI-Tools.
Si ejecutásemos el siguiente ejemplo en una máquina con GNU/Linux™:
[global] Include :%PLATFORM% [common] verbose=3 [linux-gnu] include :common runtime=linux.cfg log "Se cargo la configuración para Linux." ... [irix] runtime=irix.cfg warning "La configuración para Irix es muy precaria!" ...
Se cargaría la configuración de la sección global. No obstante se interrumpiría el proceso de esta sección para seguir procesando la sección linux-gnu. Esta a su vez interrumpiría su proceso para incluir la sección common. Una vez procesada la sección common se volvería a procesar el resto de la sección linux-gnu. Es en este momento cuando se imprime el mensaje "Se cargo la configuración para Linux". Terminada de procesar esta sección se termina de procesar la sección global justo donde se dejó al comenzar con el primer Include.
Esta sección es un poco especial. En principio, si no se indica el nombre de ninguna sección y empiezan a aparecer asignaciones, se asume que estas pertenecen a esta sección. Así podemos tener un fichero como:
verbose = 2 [global] debug = on [otra_seccion] # ...
Se interpretará que verbose
y debug
están la sección
global.
La sección global (y sólo ella) es la sección que se procesa por defecto si no se ha indicado el nombre de ninguna sección. Así si alguien ejecuta el comando Include "linux.cfg" se procesará la sección global del fichero linux.cfg ignorando el resto de secciones.
Esta sección existe por convención y no porque tenga alguna característica especial. Se aconseja crearla en el fichero de configuración global de pDI-Tools e introducir en ella los parámetros por defecto más usuales para el sistema.
Si se separan los parámetros por defecto de esta forma es posible incluirlos fácilmente desde un fichero de configuración de usuario sin tener que copiar todos los parámetros a mano a nuestro fichero de configuración.
Al igual que la sección defaults, esta también existe
por convención. Sirve para tener separada la asignación al atributo
runtime
. De esta forma es fácil poder incluir el
runtime por defecto sin tener que copiarlo del fichero
de configuración por defecto.
Mediante el comando Include es posible distribuir la configuración en diferentes ficheros de configuración, o al menos, en diferentes secciones dentro de un fichero de configuración.
Esto permite a los usuarios crear pequeños ficheros de configuración que importan partes de otros más grandes y de esta forma reciclar configuraciones. Además reciclar nos permite trabajar de una forma más ordenada y no tan propensa a errores.
Seguidamente describimos y clasificamos los parámetros de funcionamiento que se pueden fijar en los ficheros de configuración.
Siempre que no se indique lo contrario, si se redefine un parámetro, el valor vigente del parámetro será el asignado en la última aparición del mismo.
pDI-Tools incorpora su propio sistema de logs y depuración. Así evita usar librerías de terceros, polucionar lo menos posible el entorno de ejecución del programa que monitoriza e interaccionar accidentalmente con otras librerías. Seguidamente comentamos los parámetros que controlan la verbosidad y mecanismos de depuración de pDI-Tools.
logfile
- Fichero de log de pDI-Tools
Si se indica un fichero, se imprimirán en él todos los mensajes
producidos durante la ejecución de pDI-Tools sin invadir para
nada las salidas estándar (stdout
y stderr
).
Si no se indica el parámetro o se deja vacío, los mensajes de
pDI-Tools se lanzarán a la salida de error estándar (stderr
).
verbose
- Nivel de verbosidadpDI-Tools se puede configurar para que emita los mensajes según la importancia de los mismos. Este parámetro toma como valor un entero que indica el nivel de verbosidad. Cuanto mayor el valor, más detalle:
0 - Silencio total (excepto errores).
1 - Sólo se muestran advertencias y errores.
2 - Imprime todos los mensajes excepto los de depuración.
3 - Imprime toda la información de
depuración disponible (ver seguidamente el parámetro
debug
).
debug
- Modo de depuración
Si este flag está activo se realiza un montón de trabajo extra
para verificar que las cosas andan bien. No se aconseja usarlo
en producción, ya que a parte de lento, imprime demasiada
información en el log (implica poner el parámetro verbose
a 3).
Por defecto está desactivado.
Los siguientes parámetros permiten variar ciertos límites establecidos durante la inicialización de pDI-Tools. pDI-Tools hace uso en muchas ocasiones de estructuras estáticas (pools) para ganar velocidad no reservando dinámicamente memoria. Con estos parámetros podemos ajustar el consumo de memoria de pDI-Tools sin sacrificar velocidad. También es posible desactivar algunos límites y hacer que se ejecuten algoritmos que reservan memoria dinámicamente.
max_objects
- Número máximo de objetos en memoriaNúmero máximo de objetos que puede haber en memoria (entre el binario, librerías y los backends). Es conveniente que sea un número algo alto, pero tampoco demasiado grande ya que si no consumimos mucha memoria.
Por defecto es 40.
max_threads
- Número máximo de "threads"
El parámetro max_threads
fija la máxima cantidad de
"threads" que se pueden monitorizar. Es conveniente que el
número no sea demasiado bajo, ya que si se superase el número
de "threads" abortaría la ejecución del programa. Este
parámetro reserva una tabla de max_threads
punteros.
Por defecto su valor es 100.
![]() | Sólo tiene efecto si se realizan interposiciones de código mediante el mecanismo de "callback". |
num_threads
- Número de "threads" que se deben
pre-inicializar
No puede tener un valor mayor que max_threads
. Indica el
número de "threads" que se espera usar. De esta forma la
inicialización y reserva de memoria se hace al inicio del
programa y no durante la ejecución, evitando que la
instrumentación tenga mucho impacto en la ejecución del
programa.
Se pueden introducir los siguientes valores:
-1 - Inicializa todos los "threads" a
la vez (en total max_threads
) de golpe durante la
inicialización. Esto implica el peor caso en consumo de
memoria.
0 - No inicializamos ningún "thread". Se irán inicializando según se necesiten.
+x -
Inicializa x "threads". Si se
supera el número el resto se van inicializando durante la
ejecución del programa hasta un máximo de
max_threads
.
Por defecto vale 0.
![]() | Sólo tiene efecto si se realizan interposiciones de código mediante el mecanismo de "callback". |
cb_max_stubs
- Cantidad máxima de "stubs" que pueden
haber en memoriaIndica cuantos "stubs" como máximo habrá en memoria. Es importante considerar que si vamos a poner un "callback" sobre un objeto que llama a X funciones diferentes, es conveniente que este parámetro tome un valor mayor que X o si no será imposible instalar el "callback".
El valor por defecto varía según la plataforma ya que se
calcula con la función _pdi_arch_defaultMaxStubs()
. Esta
función devuelve, generalmente, el tamaño de página de la
arquitectura dividida por el tamaño de un "stub". Por ejemplo,
en INTEL® 386 esta función asigna 204 como valor por defecto a
cb_max_stubs
(4096 bytes por página y 20 bytes por
"stub").
![]() | Sólo tiene efecto si se realizan interposiciones de código mediante el mecanismo de "callback". |
cb_stack_size
- Tamaño de la pila por "thread" para las
interposiciones mediante "callback"Al monitorizar los "threads" necesitamos una zona de memoria donde guardar información sobre que se está monitorizando en un determinado momento. Evidentemente, cada "thread" ha de tener su espacio separado. Esto se realiza mediante una pila cuyo tamaño viene definido por esta variable. Si durante una monitorización hay problemas ("Segmentation Fault"), pruebe a incrementar esta variable. Si con valores grandes sigue teniendo problemas quizás debería empezar a sospechar que hay una monitorización recursiva.
Por defecto vale 1024.
![]() | Sólo tiene efecto si se realizan interposiciones de código mediante el mecanismo de "callback". |
Mediante los siguientes parámetros se construye la lista de ficheros de comandos que irán cargando los "backends" y realizando las interposiciones de código.
Estos parámetros no se comportan igual que el resto cuando se
repiten. En el caso del parámetro runtime
, se emite un error y
se termina la ejecución si se define más de una vez. En el caso del
parámetro config
, cada vez que aparece se añade el valor que le
acompaña al final de una lista.
Cuando se terminan de procesar todos los ficheros de configuración,
se antepone el valor del parámetro runtime
a la lista de
ficheros de comandos formada por los parámetros config
.
Existen también acciones (atributos sin valor) que pueden reiniciar
los valores asignados a runtime
y a config
.
Seguidamente enumeramos y explicamos estos atributos.
runtime
- Fichero de configuración de sistemaEs un fichero de comandos de reenlace pero a nivel de sistema. Es el primero en cargar de todos los ficheros de configuración, y además es único. Sólo puede haber un fichero de configuración del runtime. Generalmente sirve para cargar backends sobre funciones críticas del sistema como fork(2) o exec(2).
config
- Fichero de comandos de reenlaceIndica un fichero de comandos de reenlace. Puede indicarse este parámetro tantas veces como ficheros de comandos de reenlace se tengan.
reset_runtime
- Olvida el runtime configurado
actualmente
A este parámetro no se le puede asignar ningún valor. Cuando se
encuentra provoca que se olvide el valor asignado al parámetro
runtime
, pudiéndolo volver a definir si se desea.
reset_config
- Olvida la lista de ficheros de comandos
configurada actualmente
A este parámetro no se le puede asignar ningún valor. Cuando se
encuentra provoca que se olvide la lista de ficheros de
comandos configurada hasta el momento con el parámetro
config
.
Estos parámetros establecen rutas de búsqueda. Sirven para poder especificar los backends y ficheros de comandos sólo mediante su nombre, pDI-Tools con ayuda de estos parámetros ya tratará de resolver la ubicación de los archivos.
be_path
- Rutas de búsqueda de los backendsAquí indicamos los lugares donde podemos encontrar los backends. La sintaxis es sencilla: directorios separados por dos puntos (:).
Si se especifica varias veces este parámetro, el resultado final es como haber concatenado uno tras otro (separados por dos puntos) los distintos valores asignados.
Por defecto be_path
contiene el directorio al fichero de
runtime y al directorio de los "backends", ambos
configurados por Autoconf (generalmente estos directorios son
PREFIX/backends y
PREFIX/runtime)
becfg_path
- Rutas de búsqueda de los ficheros de
comandos
Aquí indicamos los lugares donde podemos encontrar los ficheros
de comandos de reenlace. La sintaxis y uso es igual que en el
parámetro be_path
acabado de comentar.
Por defecto becfg_path
contiene el directorio a los
ficheros de comandos de backends y al directorio de
configuración de pDI-Tools, ambos directorios configurados por
Autoconf (generalmente son
PREFIX/becfg y
PREFIX/etc).
lib_path
- Rutas de búsqueda de las librerías y DSO
(objetos compartidos).
Este parámetro indica donde se deben buscar las librerías y
DSO que se van a usar. Se inicializa con el contenido de la
variable de entorno LD_LIBRARY_PATH
y con los
directorios /lib y
/usr/lib.
Si se asigna el valor especial %LD_LIBRARY_PATH% se
sustituirá por el valor de la variable de entorno
LD_LIBRARY_PATH
.
En el resto la sintaxis y uso es igual que en los anteriores parámetros.
reset_be_path
- Olvida las rutas de búsqueda de los
backends
A este parámetro no se le puede asignar ningún valor. Cuando se
encuentra provoca que se olvide el valor asignado a los
parámetros be_path
.
reset_becfg_path
- Olvida las rutas de búsqueda de los
ficheros de comandos de reenlace
A este parámetro no se le puede asignar ningún valor. Cuando se
encuentra provoca que se olvide el valor asignado a los
parámetros becfg_path
.
reset_lib_path
- Olvida las rutas de búsqueda de las
librerías y objetos compartidos
A este parámetro no se asigna ningún valor. Cuando se encuentra
provoca que se olvide el valor asignado a los parámetros
lib_path
.
Por último explicamos algunos parámetros que no se clasifican en ninguna de las anteriores categorías.
allow_lib_as_be
- Cualquier función de cualquier DSO
puede tomar el control en una interposición de códigoEn principio está prohibido redigirir una llamada desde un DSO a otro DSO que no sea un "backend". No obstante es posible que en algún caso pueda ser interesante relajar esta restricción y esto es para lo que sirve este parámetro si se activa.
Por defecto esta opción está desactivada.
![]() | Aún así, si esta opción está activada, pDI-Tools seguirá emitiendo un mensaje de aviso cada vez que se cree un reenlace hacia una función que no pertenezca a un "backend". |
donttouch_backends
- No instrumentar los backendsSi está activada esta opción, no se podrá instrumentar sobre los "backends", o lo que es lo mismo, no se podrán hacer reenlaces sobre funciones de los "backends". Es aconsejable tener activada esta opción.
Por defecto esta opción está activada.
![]() |
Si esta opción está activada los comodines no engloban a los
backend, sino sólo a los objetos regulares (y a libpdi.so
si la opción |
donttouch_pdi
- No instrumentar a pDI-ToolsSi esta opción está desactivada se podrá instrumentar el DSO libpdi.so. Evidentemente se desaconseja seriamente desactivar esta opción a no ser que se desee, por algún motivo, que pDI-Tools se autoinstrumente.
Por defecto esta opción está activada.
cb_allow_handler
- Permitir usar un "callback handler"
alternativo al definir un "callback"
Parámetro booleano. Si está activado se permite usar un
"callback handler" propio. Si está desactivado, pDI-Tools obliga a
tener vacio o poner a NULL
el cuarto parámetro de las reglas
"callback".
Por defecto esta opción está desactivada.
no_check_on_config
- No comprueba los ficheros durante
la creación de un "script" de reenlace.Parámetro booleano. No se chequea si un objeto existe mientras se está creando una configuración de interposiciones ("script" de reenlace). Si este "flag" está activo se detectará si el objeto existe o no durante la instalación de las interposiciones.
Por defecto este "flag" está desactivado.