Back Page Slc
🛑 👉🏻👉🏻👉🏻 INFORMATION AVAILABLE CLICK HERE👈🏻👈🏻👈🏻
Удаленная работа для IT-специалистов
Софт для управления IT-инфраструктурой
Не так давно мы в компании задумали дать возможность пользователям посылать нам уведомления о произошедших ошибках в нашем ПО. Сказано — сделано. Но тут возникла задача получения backtrace-а текущего стека вызовов программы прямо в рантайме. Оказалось, что есть несколько способов решения этой задачи. Данная статья — результат моих исследований вопроса получения бэктрейса для программ написанных на С/C++ и работающих на Linux и FreeBSD.
Немного теории
В принципе, получить цепочку вызовов довольно просто. Вся необходимая информация хранится в стеке программы. Современные компиляторы для вызова функции формируют так называемые фреймы стека (stack frame). В начале каждого фрейма находится адрес предыдущего. А непосредственно перед фреймом сохранен адрес возврата, т.е. адрес инструкции, которая будет выполнена следующей, после завершения функции. Таким образом, все, что необходимо сделать — это пройти по списку фреймов и распечатать адреса возврата.
Например, это можно сделать так (пример для amd64):
void * GetReturnAddress(int depth) {
void *res;
asm (
"mov %1, %%rcx\n"
"MOVE: mov 0x0(%%rbp), %%rax\n"
"loop MOVE\n"
"mov 0x8(%%rax), %rax\n"
"mov %%rax, %0\n" : "=m" (res) : "g" (depth) : "rax", "rcx");
return res;
}
Функцию можно было написать и короче, т.к. возвращаемое значение кладется в регистр rax, можно было обойтись и без переменной res.
Но, лично для меня, наличие ассемблерных вставок не есть истинный путь джедая. Поэтому я пошел искать другое решение.
Расширения gcc
Первое, на что я набрел, это функция __builtin_return_address добродушно предоставленная нам создателями gcc. Вот выдержка из ее полного описания:
void * __builtin_return_address (unsigned int level) — возвращает адрес возврата функции. Для level=0 функция вернет адрес возврата текущей функции, для level=1 адрес возврата функции вызвавшей текущую функцию и т.д.
При ее использовании есть только одно но: функция компилируясь разворачиваются в кучу строк ассемблерного кода (чем дальше по стеку идем, тем больше строк) и в связи с этим, она не умеют принимать переменную в качестве параметра. Поэтому вместо красивой записи вида:
return __builtin_return_address(i);
приходится писать некрасивые:
switch(level) {
case 0: return __builtin_return_address(1);
case 1: return __builtin_return_address(2);
….
}
Уже лучше. Идем дальше.
backtrace
В Linux стандартная библиотека предоставляет программисту целый набор функций, позволяющих получать нужную нам информацию. В FreeBSD для этих целей необходимо установить библиотеку libexecinfo. Вот они:
int backtrace(void **buffer, int size) — функция, заполняющая buffer backtrace-ом вызывающей программы.
char **backtrace_symbols(void *const *buffer, int size) — функция, принимающая результат первой функции и транслирующая адреса функций в текстовое представление.
void backtrace_symbols_fd(void *const *buffer, int size, int fd) — делает то же самое, что и предыдущая, только вместо выделения памяти под строки через malloc пишет инфу напрямую в файл.
Для каждой функции, вошедшей в стек вызовов, backtrace_symbols возвращает строку следующего вида:
./prog(_Z6myfunci+0x1a) [0x8048840]
где: prog — имя бинарника
_Z6myfunci — закодированное имя функции
0x1a — смещение внутри функции
0x8048840 — адрес функции
Найти более подробную информацию, а также пример их использования можно в man backtrace. Хочу лишь заметить, что для того, чтобы backtrace_symbols корректно отработала, компилировать программу надо с опцией -rdynamic. Это связано с тем, что информацию об имени функции backtrace_symbols берет из таблицы динамической линковки. А по умолчанию туда попадают только функции, подгружаемые из динамических библиотек. Для принудительного добавления всех функций в эту таблицу и нужен вышеупомянутый ключ.
dladdr
Недостатком функции backtrace_symbols является то, что результат своей работы она представляет в виде текста. Т.е. если мы захотим произвести какие-либо манипуляции, например, с именем функции, то придется парсить эту строку. Опять не по-джедайски! Зачем это надо, будет понятно чуть позже.
Тут на помощь нам приходит функция dladdr. Собственно именно ее и зовет backtrace_symbols внутри себя. Её сигнатура очень проста — на вход подаем адрес функции, а на выходе получаем структуру типа Dl_info:
int dladdr(void *addr, Dl_info *info);
В случае успешного исхода вызова dladdr в структуре будет лежать все те же данные, что и в случае с backtrace_symbols.
Ну что ж, почти отлично. Теперь у нас есть адреса возвратов и даже имена функций, хоть и в закодированном формате (о решении этот вопроса поговорим чуть позже). Посмотрим, какую информацию можно еще вытащить. Может имя файла с исходным кодом и даже адрес строки где находится функция? Реально, хоть и придется заморочиться!
Что с этим делать?!
В принципе, тех данных, которые у нас уже есть, достаточно. Имея адрес, можно всегда выяснить номер строки, который породил вызов функции. Самый простой способ — использовать команду list отладчика gdb. Если у вас есть та же версия программы, собранная с дебагом, то list *<адрес> — покажет вам номер строки. А если же у вас рядом еще и исходники лежат, то вы «о чудо!» эту строку увидите.
Но идея хранить две версии программы (с дебагом и без) не соответствует джедайским стремлениям к идеальному, и поэтому я решил изучить strip. Я давно знал, что он умеет хранить бинарные файлы и отладочную информацию отдельно. Оказалось все довольно просто:
Собираем программу с дебагом как обычно (ключ -g или, для фанатов, -g3 — тогда в дебаг будут включены inline функции и всевозможные макросы).
Выполняем objcopy --only-keep-debug a.out a.out.sym. Теперь вся инфа, необходимая для комфортной работы в gdb, находится в файле a.out.sym.
Выполняем strip a.out. Т.е. удаляем дебаг из a.out.
Теперь мы можем:
Связать наш a.out.sym с a.out командой objcopy --add-gnu-debuglink=a.out.sym a.out. Тогда дебагер автоматически подгрузит всю необходимую инфу из a.out.sym, если найдет его в той же папке, в которой расположен бинарник.
Загрузить файл a.out.sym из gdb вручную командой symbol-file a.out.sym
Теперь у нас появляется возможность собирать отладочную информацию для своего ПО, но не отдавать его клиенту. Это может быть сделано из сострадания (дебаг занимает довольно внушительный объем), или из соображений безопасности (усложняем хакерам реверс инженеринг). Зато когда необходимо что-нибудь поотлаживать прямо у клиента, можно просто залить ему несколько недостающих .sym файлов.
Но, если же есть желание видеть не только номера строк, но и сам исходный код, но при этом не хотите его заливать клиенту (вполне законное желание для коммерческого ПО), можно воспользоваться gdbserver, который позволяет отлаживать программу удаленно. Для этого нужно:
На стороне клиента запустить gdbserver 127.0.0.1:2345 a.out
А со своей стороны запускаем gdb и выполняем команду target remote 127.0.0.1:2345. При этом все исходные файлы должны быть доступны с теми же путями, которые использовались в момент компиляции.
mangling/demangling
Напоследок скажу пару слов о формате записи имен функций. В двух словах подобное искажения имен функций необходимо компоновщику, чтобы решать коллизии именования. Ну, или еще проще, если в программе существуют две функции с одинаковым именем, но различными параметрами (перегруженные), то компоновщику необходимо точно знать с какой из них работать. Для этого компилятор и кодирует имя функции по особому алгоритму, назначая ей новое уникальное имя. По-английский это процесс называется mangling, а обратный ему – demangling.
Для решения задачи преобразования закодированного имени функции в оригинальный формат можно опять воспользоваться gcc-шным расширением:
char* abi::__cxa_demangle(const char* mangled_name, char* output_buffer, size_t* length, int* status)
Эта функция, принимая на вход закодированное имя функции и буфер, на выходе выдает раскодированное имя. Пример ее использования можно найти здесь.
Ну и напоследок
Самым забавным способом получения нужной информации оказалось просто спросить её у gdb. Благо последний позволяет нам это сделать (пример функции взят отсюда).
void print_trace() {
char pid_buf[30];
sprintf(pid_buf, "%d", getpid());
char name_buf[512];
name_buf[readlink("/proc/self/exe", name_buf, 511)]=0;
int child_pid = fork();
if (!child_pid) {
dup2(2,1); // redirect output to stderr
fprintf(stdout,"stack trace for %s pid=%s\n",name_buf,pid_buf);
execlp("gdb", "gdb", "--batch", "-n", "-ex", "thread", "-ex", "bt", name_buf, pid_buf, NULL);
abort(); /* If gdb failed to start */
} else {
waitpid(child_pid,NULL,0);
}
}
Все что нам нужно будет сделать, это вызвать функцию print_trace и, вуаля, стек вызовов распечатается в stdout. В принципе, вариант работающий, но очень медленный и требующий установки gdb.
Вот и все.
Приятной отладки!
Укажите причину минуса, чтобы автор поработал над ошибками
Софт для управления IT-инфраструктурой
А из обработчика сигнала GetReturnAddress() и backtrace() работают?
Да, должны (но я не пробовал. У меня задача была отлаживать возникающие exception).
По крайней мере, gdb из обработчика сигнала стек показывает, а он по нему ходит теми же методами.
Ещё один, кроссплатформенный: stacktrace.sourceforge.net
это просто удобная обертка над backtrace.
Листинг программ в картинках. На IT-ресурсе.
Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.
Дата основания
10 февраля 2004 г.
Локация
Иркутск
Россия
Сайт
ispsystem.ru
Численность
101–200 человек
Дата регистрации
19 мая 2009 г.
#ЧернаяПятница в ISPsystem! - Скидка 30% при покупке ISPmanager Lite с 27 по 29 ноября. Промокод ������blacklite2020������.… https://t.co/hn9qilZsu7
В 16 раз быстрее: контейнеры LXD против виртуальных машин на KVM. Рассказываем о результатах сравнительного тестиро… https://t.co/RcSEk4OcrI
Контейнерная виртуализация может быть такой же надёжной, как аппаратная, если это инфраструктурные LXD-контейнеры.… https://t.co/qFWvZ8d0If
12 тыс. просмотров. Уникальные посетители страницы.
3,9 тыс. дочитываний, 32%. Пользователи, дочитавшие до конца.
6,5 мин. Среднее время дочитывания публикации.
Читайте о конструктивных особенностях твердотельных накопителей. На что стоит обращать внимание при покупке SSD диска, какие у них преимущества и недостатки.
Независимо от видов устройств, все они применяются в личных и деловых целях, но основная нагрузка среди них приходится на стационарные персональные компьютеры и ноутбуки. Важным внутренним элементом современных компьютеров, отвечающим за безопасное хранение данных и мгновенный доступ к ним в любой момент времени, несомненно является внутреннее запоминающее устройство. Наиболее распространенным стационарным хранилищем информации, обеспечивающим высокую скорость чтения/записи данных и обладающим самыми большими емкостными характеристиками, является жесткий диск («HDD»). Одним из главных преимуществ, несомненно влияющим на повышенное внимание пользователей к данному хранилищу, стоит выделить относительно низкую стоимость жестких дисков при значительных объемах внутреннего полезного дискового пространства, что выражается в самой низкой себестоимости одного гигабайта информации, хранящегося на таком устройстве.
Но в последнее время большую популярность приобретает другой вид запоминающего устройства, подкупающий своими высочайшими скоростными характеристиками при обработке данных, превышающими аналогичные значения жестких дисков в несколько раз, – твердотельные накопители «SSD». Они имеют сравнительно меньшие размеры за счет другого внутреннего организационного построения, не издают шума как аналогичные жесткие диски по причине отсутствия движущихся частей, гораздо более устойчивы к внешним повреждениям (например, вызванным в результате падения накопителя) и обладают рядом функциональных особенностей, отличающих их от жестких дисков, одним из которых является уменьшенное значение внутреннего объема накопителя.
И далее мы подробнее остановимся на представлении конструктивных особенностей твердотельных накопителей и представим описание одного из образцов, относительного не дорогого варианта, современного «SSD» хранилища.
Понятие твердотельный накопитель проистекает из особенностей конструктивных элементов, с помощью которых реализуется технология записи, чтения и хранения информации. И следуя их свойствам можно утверждать, что твердотельный накопитель «SSD» («Solid State Drive») представляет собой электронное самодостаточное запоминающее устройство, способное хранить данные при отсутствии электрического питания, и полноценное функционирование которого осуществляется посредством применения комплекса внутренних микросхем и управляющего контроллера на основе полупроводниковой технологии.
Конфигурационное устройство накопителя информации «SSD»подразумевает обязательное наличие нескольких основных элементов, присутствующих в обязательном порядке в большинстве запоминающих устройств.
Самый массовый вид твердотельных накопителей построен на принципах хранения информации при помощи микросхем флэш-памяти «NAND», в зависимости от емкости накопителя имеющий один или несколько таких блоков. «NAND» флэш-память применяется для хранения пользовательских данных в блоках энергонезависимой (не требующей питания для поддержки целостности данных) памяти. Отдельным элементом может выступать память «DDR», владеющая небольшим количеством энергозависимой памяти (непосредственно требует питания для хранения данных), используемой для кэширования информации для последующего доступа, и присутствует не в каждом варианте твердотельного запоминающего устройства «SSD». И в дополнение к основной конструкции важным элементом устройства выступает контроллер, который концентрирует и обеспечивает основное соединение между «NAND» флэш-памятью и компьютерным устройством, а также содержит программную прошивку, которая отвечает за полноценное управление запоминающим накопителем «SSD». Помимо прочего, контроллер хранит сведения о том, какие и сколько раз использовались блоки памяти для сохранения информации, определяет степень их износа (истирания) и перенаправляет запись новых данных на менее изношенные блоки для более равномерной выработки памяти накопителя.
Такой технологический союз электронных компонентов позволяет применять твердотельные накопители «SSD» в качестве универсального хранилища данных как единственное запоминающее устройство (например, в неттопах, планшетах, нетбуках, коммуникаторах и т.д.), так и использовать совместно с жесткими дисками, образуя разделение приоритетов (например, операционная система установлена на твердотельном устройстве «SSD», а основной объем данных, который не предъявляет особых скоростных требований, располагается на жестком диске «HDD»). А иногда и встраиваться в общий корпус с магнитными дисками, образуя единые гибридные жесткие диски.
Благодаря своему особому строению и подходу к процессам хранения и чтения/записи информации, твердотельные накопители «SSD»получают значительные преимущества в сравнении с жесткими дисками «HDD», основные отличия которых сосредоточены в следующих параметрах:
Однако несмотря на присутствие многих факторов, существенно опережающих показатели жестких дисков по отдельным представленным параметрам, твердотельные накопители имеют ряд недостатков, ощутимо заметных в сравнении с самым массовым хранилищем данных «HDD», которые серьезно ограничивают их применение:
Однако несмотря на наличие конкретных недостатков, технологии создания полупроводниковых устройств памяти «SSD» развиваются и стремительно приближаются, по многим критериям, к полноценной замене вариантов жестких дисков «HDD», или, по крайней мере, к их равноценному обоюдному применению.
Основным элементом твердотельного накопителя, отвечающим за постоянное и безошибочное хранение данных, без преувеличения необходимо назвать «NAND» флэш-память, на описании вариантов которой необходимо остановиться более подробно.
Флэш-память «NAND» состоит из огромного множества ячеек, которые содержат биты, и они либо включаются, либо выключаются в зависимости от подачи на них электрического заряда. Способ организации ячеек флэш-памяти «NAND» обусловлен наличием управляемых транзисторов с плавающим затвором, которые регулируют принудительную подачу заряда, благодаря чему происходит процесс создания большой напряжённости электрического поля, выраженный в непосредственной записи или стирании информации в ячейки. Количество битов для записи данных в ячейках определяет наименование флэш-памяти и варьируется от одного до нескольких бит, например, флэш-память одноуровневой ячейки («SLC») содержит один бит в каждой ячейке.
Печатная плата запоминающего устройства, содержащая «NAND»память, контроллер и память «DDR», обладает соответствующими отраслевыми стандартными размерами для беспроблемной установки в разнообразные компьютерные устройства. Поэтому производители применяют технологию увеличения количества зарядов на плавающем затворе транзистора, позволяющую соответственно размещать в одной ячейке большее количество бит, увеличивая их до двух в памяти «MLC», или повышая общее количество бит в ячейке до трех в памяти «TLC». Такой подход обусловлен стремлением значительно удешевить конечный продукт и серьезно увеличить его полезную емкость, а память получила соответствующую классификацию многоуровневая.
Однако существуют конкретные причины, по которым производители, наряду с высокоемкостными накопителями на базе многоуровневых вариантов памяти, собирают флэш-память с одним битом на ячейку, например, одноуровневая память «SLC». Преимущество «SLC»заключается в том, что память является самой быстрой и долговечной из представленных образцов, но имеет недостатки, получившие свое отражение в более затратном производстве и сравнительно малой конечной емкости хранения в гигабайтах. Поэтому «SLC»предпочтительнее для интенсивного использования на предприятиях, готовых потратить значительные средства на покупку дорогостоящих запоминающих устройств на базе одноуровневой памяти.
Флэш-память с использованием технологии «MLC» и «TLC» по сравнению с «SLC» ощутимо дешевле в производстве и представлена с более высокой емкостью хранения, но за счет сравнительно короткого срока службы, более медленных скоростей и увеличенного времени доступа предлагает значительно меньшее количество гарантированных циклов чтения/записи информации. Поэтому твердотельные накопители «SSD» на основе памяти «MLC» и «TLC» предпочтительны для повседневного использования на потребительских компьютерных устройствах.
Осознание собственных потребностей в определенных вычислительных мощностях и понимание основ устройства и функционирования флэш-памяти «NAND» не только поможет пользователям выбрать правильный вариант накопителя «SSD», учитывая разнообразные критерии отбора (например, обоснованная себестоимость одного гигабайта информации, выраженного в цене продукта), но и позволит более ответственно подходить к вопросу его эксплуатации.
Плотное расположение ячеек на пластине флэш-памяти «NAND», последующее уменьшение их размера для увеличения общего количества, влияющее на емкость устройства, ограничены единичным размером ячейки, который на сегодняшний день, по ряду причин, не может быть менее 14 нм.
Однако существующая эффективность и емкость твердотельных накопителей «SSD» требует увеличения общего числа ячеек. С этой целью ведущие производители кристаллов «NAND» памяти разработали и применили технологию вертикального расположения слоев кристаллов в одной плоскости, начав с нескольких слоев для увеличения плотности и успешно освоив создание 96-слойной памяти.
Такой вариант памяти получил название «3D NAND», применение которой позволило существенно повысить емкость кристалла и вернуть техпроцессы производства к более привычным и менее затратным величинам.
Благодаря внесенным изменениям объем конечных твердотельных накопителей существенно возрос, а скорость обработки
Особенности работы SSD — что такое SLC-кэш и как он влияет на скорость
Как я за backtrace-ом ходил / Блог компании ISPsystem / Хабр
Как выбрать SSD диск и что такое «SLC», «MLC», «TLC», «3D NAND»?
Backpage - Wikipedia
# Gloria Tells feat. SLCT – Looking Back (SLCT Remix)
Call Girls In Doha
Tulsa Sensual Massage
Barbies Escorts
Back Page Slc